In this document we present the joint analysis of the PASS1A metabolomics datasets.
Load all datasets and setup the session
Load the data from the cloud, including: phenotypic data, metabolomic datasets, and metabolomics dictionary.
source("~/Desktop/repos/motrpac-bic-norm-qc/tools/supervised_normalization_functions.R")
source("~/Desktop/repos/motrpac-bic-norm-qc/tools/unsupervised_normalization_functions.R")
source("~/Desktop/repos/motrpac-bic-norm-qc/tools/gcp_functions.R")
source("~/Desktop/repos/motrpac-bic-norm-qc/tools/association_analysis_methods.R")
source("~/Desktop/repos/motrpac-bic-norm-qc/tools/data_aux_functions.R")
source("~/Desktop/repos/motrpac/tools/prediction_ml_tools.R")
library(randomForest) # for classification tests
# Load the dmaqc data
merged_dmaqc_data = load_from_bucket("merged_dmaqc_data2019-10-15.RData",
"gs://bic_data_analysis/pass1a/pheno_dmaqc/",F)
merged_dmaqc_data = merged_dmaqc_data[[1]]
rownames(merged_dmaqc_data) = as.character(merged_dmaqc_data$vial_label)
# define the tissue variable
merged_dmaqc_data$tissue = merged_dmaqc_data$sampletypedescription
# define the time to freeze variable
merged_dmaqc_data$time_to_freeze = merged_dmaqc_data$calculated.variables.time_death_to_collect_min +
merged_dmaqc_data$calculated.variables.time_collect_to_freeze_min
# Add blood freeze times
blood_samples =
merged_dmaqc_data$specimen.processing.sampletypedescription ==
"EDTA Plasma"
blood_freeze_time =
as.difftime(merged_dmaqc_data$specimen.processing.t_freeze,units = "mins") -
as.difftime(merged_dmaqc_data$specimen.processing.t_edtaspin,units="mins")
blood_freeze_time = as.numeric(blood_freeze_time)
merged_dmaqc_data$time_to_freeze[blood_samples] =
blood_freeze_time[blood_samples]
# Load our parsed metabolomics datasets
metabolomics_bucket_obj = load_from_bucket(
file = "metabolomics_parsed_datasets_pass1a_external1.RData",
bucket = "gs://bic_data_analysis/pass1a/metabolomics/")
metabolomics_parsed_datasets = metabolomics_bucket_obj[[1]]
# Load the processed, merged, normalized datasets
metabolomics_processed_datasets = load_from_bucket(
file="metabolomics_processed_datasets11182019.RData",
bucket = "gs://bic_data_analysis/pass1a/metabolomics/"
)[[1]]
# Define the variables to be adjusted for:
biospec_cols = c(
"acute.test.distance",
"calculated.variables.time_to_freeze",
# "calculated.variables.edta_coll_time", # no need - see code above for blood
"bid" # required for matching datasets
)
differential_analysis_cols = c(
"animal.registration.sex",
"animal.key.timepoint",
"animal.key.is_control"
)
pipeline_qc_cols = c("sample_order")
Log-transform: effect on variance
Some sites do not use the log transformation on their dataset. In this section we plot the coefficient of variation as a function of the mean instensity. We take a single dataset as an example to show how log-transformed data have reduced dependency and smoother plots.
As an additional analysis we also plot the number of missing values per metabolite as a function of its mean intensity. We show that while there is high correlation some missing values appear in fairely high intensities. This is important for imputation as some sites use some fixed low value instead of knn imputation.
# Plot cv vs means
library(gplots)
d = metabolomics_parsed_datasets[["white_adipose_powder,metab_u_hilicpos,unnamed"]]
dx = d$sample_data
CoV<-function(x){return(sd(x,na.rm = T)/mean(x,na.rm=T))}
dmeans = apply(dx,1,mean,na.rm=T)
CoVs = apply(dx,1,CoV)
inds = !is.na(CoVs)
df = data.frame(Mean_intensity = dmeans[inds],CoV = CoVs[inds])
plot(CoV~Mean_intensity,df,cex=0.5,pch=20,main="Raw data")
lines(lowess(CoV~Mean_intensity,df),lty=2,lwd=2,col="blue")

# Repeat after log2
dx = log(1+d$sample_data,base=2)
dmeans = apply(dx,1,mean,na.rm=T)
CoVs = apply(dx,1,CoV)
inds = !is.na(CoVs)
df = data.frame(Mean_intensity = dmeans[inds],CoV = CoVs[inds])
plot(CoV~Mean_intensity,df,cex=0.5,pch=20,main="Log2 data")
lines(lowess(CoV~Mean_intensity,df),lty=2,lwd=2,col="blue")

# Plot number of NAs vs intensity mean
dx = log(1+d$sample_data,base=2)
dmeans = apply(dx,1,mean,na.rm=T)
num_nas = rowSums(is.na(dx))
df = data.frame(Num_NAs = num_nas[inds],Mean_intensity = dmeans[inds])
rho = cor(df$Num_NAs,df$Mean_intensity,method="spearman")
rho = format(rho,digits=2)
plot(Num_NAs~Mean_intensity,df,cex=0.5,pch=20,
main=paste("Spearman:",rho))

Targeted vs. untargeted: comparison as a prediction task
Use 5-fold cross validation for analysis within tissues. For each pair of targeted and untargeted datasets from the same tissue, we use the untargeted data as the predictive features and all metabolites in the targeted datasets as the dependent variables. The code below uses feature selection and random forests to train the predictive models.
nfolds = 5
prediction_analysis_results = list()
for(nn1 in names(metabolomics_processed_datasets)){
nn1_tissue = strsplit(nn1,split=",")[[1]][1]
nn1_tissue = gsub("_powder","",nn1_tissue)
if(grepl("untargeted",nn1)){next}
for(nn2 in names(metabolomics_processed_datasets)){
if(nn2 == nn1){next}
if(!grepl("untargeted",nn2)){next}
nn2_tissue = strsplit(nn2,split=",")[[1]][1]
nn2_tissue = gsub("_powder","",nn2_tissue)
nn2_dataset = strsplit(nn2,split=",")[[1]][2]
if(nn1_tissue!=nn2_tissue){next}
print(paste("features from:",nn2))
print(paste("labels from:",nn1))
# get the numeric datasets and their annotation
y = metabolomics_processed_datasets[[nn1]]$sample_data
x = metabolomics_processed_datasets[[nn2]]$sample_data
# align the sample sets
bid_y = merged_dmaqc_data[colnames(y),"bid"]
bid_x = merged_dmaqc_data[colnames(x),"bid"]
# step 1: merge samples from the same BID
if(length(unique(bid_x))!=length(bid_x)){
x = aggregate_repeated_samples(x,bid_x)
}
else{
colnames(x) = bid_x
}
if(length(unique(bid_y))!=length(bid_y)){
y = aggregate_repeated_samples(y,bid_y)
}else{
colnames(y) = bid_y
}
# step 2: use the shared bio ids
shared_bids = as.character(intersect(colnames(y),colnames(x)))
x = t(as.matrix(x[,shared_bids]))
y = t(as.matrix(y[,shared_bids]))
# At this point x and y are over the same BIDs, now we add the metadata
y_meta = unique(metabolomics_processed_datasets[[nn1]]$sample_meta_parsed)
rownames(y_meta) = y_meta$bid
y_meta = y_meta[shared_bids,]
# take the covariates (ignore distances)
curr_cov_cols = intersect(colnames(y_meta),biospec_cols[2])
curr_covs = data.frame(y_meta[,curr_cov_cols])
names(curr_covs) = curr_cov_cols
curr_covs$sex = y_meta$animal.registration.sex # add sex
# add the covariates into x
x = cbind(x,curr_covs)
# Run the regressions
folds = sample(rep(1:nfolds,(1+nrow(x)/nfolds)))[1:nrow(x)]
numFeatures = min(ncol(x),2000)
preds = c();real=c()
for(i in 1:ncol(y)){
if( i %% 10 == 0){print(paste("analyzing metabolite number:",i))}
y_i = y[,1]
i_preds = c();i_real=c()
for(j in 1:nfolds){
tr_x = x[folds!=j,]
tr_yi = y_i[folds!=j]
te_x = x[folds==j,]
te_y = y_i[folds==j]
# random forest
# model = randomForest(tr_yi,x=tr_x,ntree = 20)
# te_preds = predict(model,newdata = te_x)
model = feature_selection_wrapper(tr_x,tr_yi,
coeff_of_var,randomForest,
topK = numFeatures,ntree=50)
te_preds = predict(model,newdata = te_x)
i_preds = c(i_preds,te_preds)
i_real = c(i_real,te_y)
}
preds = cbind(preds,i_preds)
real = cbind(real,i_real)
}
colnames(preds) = colnames(y)
colnames(reals) = colnames(y)
currname = paste(nn1,nn2,sep=";")
prediction_analysis_results[[currname]] = list(
preds = preds,real=real
)
}
}
save_to_bucket(prediction_analysis_results,
file="tar_vs_untar_prediction_analysis_results.RData",
bucket = "gs://bic_data_analysis/pass1a/metabolomics/")
We now take the predicted and real values and estimate the prediction accuracy in different ways.
prediction_analysis_results = load_from_bucket(
"tar_vs_untar_prediction_analysis_results.RData",
"gs://bic_data_analysis/pass1a/metabolomics/",F)
prediction_analysis_results = prediction_analysis_results[[1]]
results_metrics = list()
for(nn in names(prediction_analysis_results)){
preds = prediction_analysis_results[[nn]]$preds
real = prediction_analysis_results[[nn]]$real
tar_name = strsplit(nn,split=";")[[1]][1]
untar_name = strsplit(nn,split=";")[[1]][2]
y = metabolomics_processed_datasets[[tar_name]]$sample_data
colnames(preds) = rownames(y)
colnames(real) = rownames(y)
tar_name = simplify_metab_dataset_name(tar_name)
untar_name = simplify_metab_dataset_name(untar_name)
currtissue = strsplit(tar_name,split=",")[[1]][1]
tar_name = gsub(paste(currtissue,",",sep=""),"",tar_name)
untar_name = gsub(paste(currtissue,",",sep=""),"",untar_name)
if(! currtissue %in% names(results_metrics)){
results_metrics[[currtissue]] = list()
}
if(! tar_name %in% names(results_metrics[[currtissue]])){
results_metrics[[currtissue]][[tar_name]] = list()
}
rhos = format(diag(cor(preds,real,method="spearman")),digits=3)
rhos = as.numeric(rhos)
SEs = colSums((preds-real)^2)
MSEs = SEs / nrow(preds)
RMSE = sqrt(MSEs)
rMSE = MSEs / apply(y,1,var)
CoVs = apply(y,1,sd) / apply(y,1,mean)
discCoVs = cut(CoVs,breaks = 2,ordered_result = T)
results_metrics[[currtissue]][[tar_name]][[untar_name]] = data.frame(
rhos,MSEs,RMSE,rMSE,CoVs,discCoVs
)
}
We now present a few summary plots.
for(tissue in names(results_metrics)){
for(tar in names(results_metrics[[tissue]])){
l = results_metrics[[tissue]][[tar]]
rho_vs_cv = c()
for(untar in names(l)){
m = l[[untar]][,c("rhos","discCoVs")] # take the current matrix
m = cbind(rep(untar,nrow(m)),m)
m$discCoVs = as.numeric(m$discCoVs)
rho_vs_cv = rbind(rho_vs_cv,m)
}
colnames(rho_vs_cv)[1] = "dataset"
boxplot(rhos~discCoVs:dataset,data=rho_vs_cv,las=2,
ylab="Spearman",xlab = "",ylim=c(0,1),
main = paste(tissue,tar,sep=","))
}
}
As additional references, we train below additional models. First, we check the prediction of naive models that use technical and clinical covariates only. Second, we use multi-task regression and deep learning models.
cov_prediction_analysis_results = list()
for(nn1 in names(metabolomics_processed_datasets)){
nn1_tissue = strsplit(nn1,split=",")[[1]][1]
nn1_tissue = gsub("_powder","",nn1_tissue)
if(grepl("untargeted",nn1)){next}
print(nn1)
y = metabolomics_processed_datasets[[nn1]]$sample_data
y_vials = colnames(y)
bid_y = merged_dmaqc_data[colnames(y),"bid"]
colnames(y) = bid_y
y = t(as.matrix(y))
if(ncol(y)>1000){next}
cov_cols = c("animal.registration.sex",
"acute.test.weight",
"acute.test.distance",
"animal.key.timepoint")
covs = merged_dmaqc_data[y_vials,cov_cols]
x = covs
# Run the regressions
folds = sample(rep(1:nfolds,(1+nrow(x)/nfolds)))[1:nrow(x)]
numFeatures = min(ncol(x),2000)
preds = c();real=c()
for(i in 1:ncol(y)){
y_i = y[,1]
i_preds = c();i_real=c()
for(j in 1:nfolds){
print(j)
tr_x = x[folds!=j,]
tr_yi = y_i[folds!=j]
te_x = x[folds==j,]
te_y = y_i[folds==j]
# random forest
model = randomForest(tr_yi,x=tr_x,ntree = 20)
te_preds = predict(model,newdata = te_x)
i_preds = c(i_preds,te_preds)
i_real = c(i_real,te_y)
}
preds = cbind(preds,i_preds)
real = cbind(real,i_real)
}
cov_prediction_analysis_results[[nn1]] = list(
preds = preds,real=real
)
}
# preds = c();real=c()
# for(j in 1:nfolds){
# tr_x = x[folds!=j,]
# tr_y = y[folds!=j,]
# te_x = x[folds==j,]
# te_y = y[folds==j,]
# model = MTL_wrapper(tr_x,tr_y,type="Regression", Regularization="L21")
# te_preds = predict(model,te_x)
# real = rbind(real,te_y)
# preds = rbind(preds,te_preds)
# }
# diag(cor(preds,real))
# Using PLS regression
# library(pls)
# pls_model = plsr(y~x,ncomp = 5,validation="LOO")
# eval = MSEP(pls_model)
#
# y_pca = prcomp(y)
# plot(y_pca)
# explained_var = y_pca$sdev^2/sum(y_pca$sdev^2)
# y_pca_matrix = y_pca$x[,1:10]
#
# # regress out sex, weight
#
# get_explained_variance_using_PCA(x,y)
# x = apply(x,2,regress_out,covs=covs)
# y = apply(y,2,regress_out,covs=covs)
# get_explained_variance_using_PCA(x,y)
LS0tCnRpdGxlOiAiQklDIG1ldGFib2xvbWljcyBkYXRhIGFuYWx5c2lzIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBwZGZfZG9jdW1lbnQ6CiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwotLS0KCkluIHRoaXMgZG9jdW1lbnQgd2UgcHJlc2VudCB0aGUgam9pbnQgYW5hbHlzaXMgb2YgdGhlIFBBU1MxQSBtZXRhYm9sb21pY3MgZGF0YXNldHMuCgojIExvYWQgYWxsIGRhdGFzZXRzIGFuZCBzZXR1cCB0aGUgc2Vzc2lvbgoKTG9hZCB0aGUgZGF0YSBmcm9tIHRoZSBjbG91ZCwgaW5jbHVkaW5nOiBwaGVub3R5cGljIGRhdGEsIG1ldGFib2xvbWljIGRhdGFzZXRzLCBhbmQgbWV0YWJvbG9taWNzIGRpY3Rpb25hcnkuCgpgYGB7cixyZXN1bHRzPSdoaWRlJyxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9CnNvdXJjZSgifi9EZXNrdG9wL3JlcG9zL21vdHJwYWMtYmljLW5vcm0tcWMvdG9vbHMvc3VwZXJ2aXNlZF9ub3JtYWxpemF0aW9uX2Z1bmN0aW9ucy5SIikKc291cmNlKCJ+L0Rlc2t0b3AvcmVwb3MvbW90cnBhYy1iaWMtbm9ybS1xYy90b29scy91bnN1cGVydmlzZWRfbm9ybWFsaXphdGlvbl9mdW5jdGlvbnMuUiIpCnNvdXJjZSgifi9EZXNrdG9wL3JlcG9zL21vdHJwYWMtYmljLW5vcm0tcWMvdG9vbHMvZ2NwX2Z1bmN0aW9ucy5SIikKc291cmNlKCJ+L0Rlc2t0b3AvcmVwb3MvbW90cnBhYy1iaWMtbm9ybS1xYy90b29scy9hc3NvY2lhdGlvbl9hbmFseXNpc19tZXRob2RzLlIiKQpzb3VyY2UoIn4vRGVza3RvcC9yZXBvcy9tb3RycGFjLWJpYy1ub3JtLXFjL3Rvb2xzL2RhdGFfYXV4X2Z1bmN0aW9ucy5SIikKc291cmNlKCJ+L0Rlc2t0b3AvcmVwb3MvbW90cnBhYy90b29scy9wcmVkaWN0aW9uX21sX3Rvb2xzLlIiKQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkgIyBmb3IgY2xhc3NpZmljYXRpb24gdGVzdHMKCiMgTG9hZCB0aGUgZG1hcWMgZGF0YQptZXJnZWRfZG1hcWNfZGF0YSA9ICBsb2FkX2Zyb21fYnVja2V0KCJtZXJnZWRfZG1hcWNfZGF0YTIwMTktMTAtMTUuUkRhdGEiLAogICAgImdzOi8vYmljX2RhdGFfYW5hbHlzaXMvcGFzczFhL3BoZW5vX2RtYXFjLyIsRikKbWVyZ2VkX2RtYXFjX2RhdGEgPSBtZXJnZWRfZG1hcWNfZGF0YVtbMV1dCnJvd25hbWVzKG1lcmdlZF9kbWFxY19kYXRhKSA9IGFzLmNoYXJhY3RlcihtZXJnZWRfZG1hcWNfZGF0YSR2aWFsX2xhYmVsKQojIGRlZmluZSB0aGUgdGlzc3VlIHZhcmlhYmxlCm1lcmdlZF9kbWFxY19kYXRhJHRpc3N1ZSA9IG1lcmdlZF9kbWFxY19kYXRhJHNhbXBsZXR5cGVkZXNjcmlwdGlvbgojIGRlZmluZSB0aGUgdGltZSB0byBmcmVlemUgdmFyaWFibGUKbWVyZ2VkX2RtYXFjX2RhdGEkdGltZV90b19mcmVlemUgPSBtZXJnZWRfZG1hcWNfZGF0YSRjYWxjdWxhdGVkLnZhcmlhYmxlcy50aW1lX2RlYXRoX3RvX2NvbGxlY3RfbWluICsgCiAgbWVyZ2VkX2RtYXFjX2RhdGEkY2FsY3VsYXRlZC52YXJpYWJsZXMudGltZV9jb2xsZWN0X3RvX2ZyZWV6ZV9taW4KCiMgQWRkIGJsb29kIGZyZWV6ZSB0aW1lcwpibG9vZF9zYW1wbGVzID0gCiAgbWVyZ2VkX2RtYXFjX2RhdGEkc3BlY2ltZW4ucHJvY2Vzc2luZy5zYW1wbGV0eXBlZGVzY3JpcHRpb24gPT0KICAiRURUQSBQbGFzbWEiCmJsb29kX2ZyZWV6ZV90aW1lID0gCiAgYXMuZGlmZnRpbWUobWVyZ2VkX2RtYXFjX2RhdGEkc3BlY2ltZW4ucHJvY2Vzc2luZy50X2ZyZWV6ZSx1bml0cyA9ICJtaW5zIikgLQogIGFzLmRpZmZ0aW1lKG1lcmdlZF9kbWFxY19kYXRhJHNwZWNpbWVuLnByb2Nlc3NpbmcudF9lZHRhc3Bpbix1bml0cz0ibWlucyIpCmJsb29kX2ZyZWV6ZV90aW1lID0gYXMubnVtZXJpYyhibG9vZF9mcmVlemVfdGltZSkKbWVyZ2VkX2RtYXFjX2RhdGEkdGltZV90b19mcmVlemVbYmxvb2Rfc2FtcGxlc10gPSAKICBibG9vZF9mcmVlemVfdGltZVtibG9vZF9zYW1wbGVzXQoKIyBMb2FkIG91ciBwYXJzZWQgbWV0YWJvbG9taWNzIGRhdGFzZXRzCm1ldGFib2xvbWljc19idWNrZXRfb2JqID0gbG9hZF9mcm9tX2J1Y2tldCgKICBmaWxlID0gIm1ldGFib2xvbWljc19wYXJzZWRfZGF0YXNldHNfcGFzczFhX2V4dGVybmFsMS5SRGF0YSIsCiAgYnVja2V0ID0gImdzOi8vYmljX2RhdGFfYW5hbHlzaXMvcGFzczFhL21ldGFib2xvbWljcy8iKQptZXRhYm9sb21pY3NfcGFyc2VkX2RhdGFzZXRzID0gbWV0YWJvbG9taWNzX2J1Y2tldF9vYmpbWzFdXQoKIyBMb2FkIHRoZSBwcm9jZXNzZWQsIG1lcmdlZCwgbm9ybWFsaXplZCBkYXRhc2V0cwptZXRhYm9sb21pY3NfcHJvY2Vzc2VkX2RhdGFzZXRzID0gbG9hZF9mcm9tX2J1Y2tldCgKICBmaWxlPSJtZXRhYm9sb21pY3NfcHJvY2Vzc2VkX2RhdGFzZXRzMTExODIwMTkuUkRhdGEiLAogIGJ1Y2tldCA9ICJnczovL2JpY19kYXRhX2FuYWx5c2lzL3Bhc3MxYS9tZXRhYm9sb21pY3MvIgopW1sxXV0KCiMgRGVmaW5lIHRoZSB2YXJpYWJsZXMgdG8gYmUgYWRqdXN0ZWQgZm9yIChkaWZmZXJlbnRpYWwgYW5hbHlzaXMpOgpiaW9zcGVjX2NvbHMgPSBjKAogICJhY3V0ZS50ZXN0LmRpc3RhbmNlIiwKICAiY2FsY3VsYXRlZC52YXJpYWJsZXMudGltZV90b19mcmVlemUiLAogICJiaWQiICMgcmVxdWlyZWQgZm9yIG1hdGNoaW5nIGRhdGFzZXRzCiAgKQpkaWZmZXJlbnRpYWxfYW5hbHlzaXNfY29scyA9IGMoCiAgImFuaW1hbC5yZWdpc3RyYXRpb24uc2V4IiwKICAiYW5pbWFsLmtleS50aW1lcG9pbnQiLAogICJhbmltYWwua2V5LmlzX2NvbnRyb2wiCikKcGlwZWxpbmVfcWNfY29scyA9IGMoInNhbXBsZV9vcmRlciIpCmBgYAoKIyBMb2ctdHJhbnNmb3JtOiBlZmZlY3Qgb24gdmFyaWFuY2UKClNvbWUgc2l0ZXMgZG8gbm90IHVzZSB0aGUgbG9nIHRyYW5zZm9ybWF0aW9uIG9uIHRoZWlyIGRhdGFzZXQuIEluIHRoaXMgc2VjdGlvbiB3ZSBwbG90IHRoZSBjb2VmZmljaWVudCBvZiB2YXJpYXRpb24gYXMgYSBmdW5jdGlvbiBvZiB0aGUgbWVhbiBpbnN0ZW5zaXR5LiBXZSB0YWtlIGEgc2luZ2xlIGRhdGFzZXQgYXMgYW4gZXhhbXBsZSB0byBzaG93IGhvdyBsb2ctdHJhbnNmb3JtZWQgZGF0YSBoYXZlIHJlZHVjZWQgZGVwZW5kZW5jeSBhbmQgc21vb3RoZXIgcGxvdHMuCgpBcyBhbiBhZGRpdGlvbmFsIGFuYWx5c2lzIHdlIGFsc28gcGxvdCB0aGUgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzIHBlciBtZXRhYm9saXRlIGFzIGEgZnVuY3Rpb24gb2YgaXRzIG1lYW4gaW50ZW5zaXR5LiBXZSBzaG93IHRoYXQgd2hpbGUgdGhlcmUgaXMgaGlnaCBjb3JyZWxhdGlvbiBzb21lIG1pc3NpbmcgdmFsdWVzIGFwcGVhciBpbiBmYWlyZWx5IGhpZ2ggaW50ZW5zaXRpZXMuIFRoaXMgaXMgaW1wb3J0YW50IGZvciBpbXB1dGF0aW9uIGFzIHNvbWUgc2l0ZXMgdXNlIHNvbWUgZml4ZWQgbG93IHZhbHVlIGluc3RlYWQgb2Yga25uIGltcHV0YXRpb24uCgpgYGB7cixvdXQuaGVpZ2h0PSc1MCUnLG91dC53aWR0aD0nNTAlJyxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9CgojIFBsb3QgY3YgdnMgbWVhbnMKbGlicmFyeShncGxvdHMpCmQgPSBtZXRhYm9sb21pY3NfcGFyc2VkX2RhdGFzZXRzW1sid2hpdGVfYWRpcG9zZV9wb3dkZXIsbWV0YWJfdV9oaWxpY3Bvcyx1bm5hbWVkIl1dCmR4ID0gZCRzYW1wbGVfZGF0YQpDb1Y8LWZ1bmN0aW9uKHgpe3JldHVybihzZCh4LG5hLnJtID0gVCkvbWVhbih4LG5hLnJtPVQpKX0KZG1lYW5zID0gYXBwbHkoZHgsMSxtZWFuLG5hLnJtPVQpCkNvVnMgPSBhcHBseShkeCwxLENvVikKaW5kcyA9ICFpcy5uYShDb1ZzKQpkZiA9IGRhdGEuZnJhbWUoTWVhbl9pbnRlbnNpdHkgPSBkbWVhbnNbaW5kc10sQ29WID0gQ29Wc1tpbmRzXSkKcGxvdChDb1Z+TWVhbl9pbnRlbnNpdHksZGYsY2V4PTAuNSxwY2g9MjAsbWFpbj0iUmF3IGRhdGEiKQpsaW5lcyhsb3dlc3MoQ29Wfk1lYW5faW50ZW5zaXR5LGRmKSxsdHk9Mixsd2Q9Mixjb2w9ImJsdWUiKQoKIyBSZXBlYXQgYWZ0ZXIgbG9nMgpkeCA9IGxvZygxK2Qkc2FtcGxlX2RhdGEsYmFzZT0yKQpkbWVhbnMgPSBhcHBseShkeCwxLG1lYW4sbmEucm09VCkKQ29WcyA9IGFwcGx5KGR4LDEsQ29WKQppbmRzID0gIWlzLm5hKENvVnMpCmRmID0gZGF0YS5mcmFtZShNZWFuX2ludGVuc2l0eSA9IGRtZWFuc1tpbmRzXSxDb1YgPSBDb1ZzW2luZHNdKQpwbG90KENvVn5NZWFuX2ludGVuc2l0eSxkZixjZXg9MC41LHBjaD0yMCxtYWluPSJMb2cyIGRhdGEiKQpsaW5lcyhsb3dlc3MoQ29Wfk1lYW5faW50ZW5zaXR5LGRmKSxsdHk9Mixsd2Q9Mixjb2w9ImJsdWUiKQoKIyBQbG90IG51bWJlciBvZiBOQXMgdnMgaW50ZW5zaXR5IG1lYW4KZHggPSBsb2coMStkJHNhbXBsZV9kYXRhLGJhc2U9MikKZG1lYW5zID0gYXBwbHkoZHgsMSxtZWFuLG5hLnJtPVQpCm51bV9uYXMgPSByb3dTdW1zKGlzLm5hKGR4KSkKZGYgPSBkYXRhLmZyYW1lKE51bV9OQXMgPSBudW1fbmFzW2luZHNdLE1lYW5faW50ZW5zaXR5ID0gZG1lYW5zW2luZHNdKQpyaG8gPSBjb3IoZGYkTnVtX05BcyxkZiRNZWFuX2ludGVuc2l0eSxtZXRob2Q9InNwZWFybWFuIikKcmhvID0gZm9ybWF0KHJobyxkaWdpdHM9MikKcGxvdChOdW1fTkFzfk1lYW5faW50ZW5zaXR5LGRmLGNleD0wLjUscGNoPTIwLAogICAgIG1haW49cGFzdGUoIlNwZWFybWFuOiIscmhvKSkKCgpgYGAKCgojIExvZy10cmFuc2Zvcm06IGVmZmVjdCBvbiBkaWZmZXJlbnRpYWwgYW5hbHlzaXMKCkNvcnJlY3QgdGhlIG1ldGEtZGF0YSwgZm9yIHRoZSBkaWZmZXJlbnRpYWwgYW5hbHlzaXM6CmBgYHtyLGV2YWw9VFJVRSxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9CmZvcihkYXRhc2V0bmFtZSBpbiBuYW1lcyhtZXRhYm9sb21pY3NfcHJvY2Vzc2VkX2RhdGFzZXRzKSl7CiAgY3Vycl9kYXRhID0gbWV0YWJvbG9taWNzX3Byb2Nlc3NlZF9kYXRhc2V0c1tbZGF0YXNldG5hbWVdXSRzYW1wbGVfZGF0YQogIGN1cnJfbWV0YSA9IG1ldGFib2xvbWljc19wcm9jZXNzZWRfZGF0YXNldHNbW2RhdGFzZXRuYW1lXV0kc2FtcGxlX21ldGFfcGFyc2VkCiAgIyBvcmdhbml6ZSB0aGUgbWV0YWRhdGEKICBjdXJyX21ldGEgPSBjdXJyX21ldGFbLHVuaW9uKGJpb3NwZWNfY29scyxkaWZmZXJlbnRpYWxfYW5hbHlzaXNfY29scyldCiAgIyByZW1vdmUgbWV0YWRhdGEgdmFyaWFibGVzIHdpdGggdG9vIG1hbnkgTkFzCiAgbmFfY291bnRzID0gYXBwbHkoaXMubmEoY3Vycl9tZXRhKSwyLHN1bSkKICBjdXJyX21ldGEgPSBjdXJyX21ldGFbLG5hX2NvdW50cy9ucm93KGN1cnJfbWV0YSkgPCAwLjFdCiAgbWV0YWJvbG9taWNzX3Byb2Nlc3NlZF9kYXRhc2V0c1tbZGF0YXNldG5hbWVdXSRzYW1wbGVfbWV0YV9wYXJzZWQgPSBjdXJyX21ldGEKfQpgYGAgIAoKVW50YXJnZXRlZCBkYXRhIGFyZSB0eXBpY2FsbHkgbG9nLXRyYW5zZm9ybWVkIGFuZCBhbmFseXplZCB1c2luZyBsaW5lYXIgbW9kZWxzLiBPbiB0aGUgb3RoZXIgaGFuZCwgY29uY2VudHJhdGlvbiBkYXRhIGFyZSBzb21ldGltZXMgYW5hbHl6ZWQgd2l0aCB0aGUgc2FtZSB0eXBlIG9mIG1vZGVscyBidXQgdXNpbmcgdGhlIG9yaWdpbmFsIGRhdGEuIFRoaXMgcmFpc2VzIGEgcHJvYmxlbSBpZiB3ZSB3aXNoIHRvIGNvbXBhcmUgZXhhY3Qgc3RhdGlzdGljcyBmcm9tIHRoZXNlIGRhdGEuIEluIHRoaXMgc2VjdGlvbiB3ZSBwZXJmb3JtIHJlc2lkdWFsIGFuYWx5c2lzIGZvciBzaW5nbGUgbWV0YWJvbGl0ZXMuIE91ciBnb2FsIGlzIHRvIGlkZW50aWZ5IGlmIGNvbmNlbnRyYXRpb24gZGF0YSBiZWhhdmVzICJub3JtYWxseSIgd2hlbiBub3QgbG9nLXRyYW5zZm9ybWVkLiBUaGUgYW5hbHlzaXMgYmVsb3cgZXhhbWluZXMgdGhlIHJlc2lkdWFscyBvZiB0aGUgZGF0YSBhZnRlciBmaXR0aW5nIGxpbmVhciBtb2RlbHMgZm9yIGVhY2ggbWV0YWJvbGl0ZSwgYWRqdXN0aW5nIGZvciBmcmVlemUgdGltZSBhbmQgc2V4LiBXZSB0aGVuIGNvbXBhcmUgdGhlIHJlc3VsdHMgd2l0aCBhbmQgd2l0aG91dCB0aGUgbG9nLXRyYW5zZm9ybWF0aW9uLCBjb3VudGluZyB0aGUgbnVtYmVyIG9mIG1ldGFib2xpdGVzIHdpdGggYSBzaWduaWZpY2FudCBldmlkZW5jZSBmb3Igbm9uLW5vcm1hbGx5IGRpc3RyaWJ1dGVkIHJlc2lkdWFscy4gCgpgYGB7cixvdXQuaGVpZ2h0PSc1MCUnLG91dC53aWR0aD0nNTAlJyxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9CiMgY2hlY2sgZm9yIG5vcm1hbGl0eSB1c2luZyB0aGUgS29sbW9nb3Jvdi1TbWlybm92IHRlc3QKaXNfbm9ybWFsX3Rlc3Q8LWZ1bmN0aW9uKHYsc2FtcD0xMDAwMCl7CiAgcmV0dXJuKGtzLnRlc3QodiwicG5vcm0iLG1lYW4odixuYS5ybT1UKSxzZCh2LG5hLnJtID0gVCkpJHAudmFsdWUpCn0KIyBnbyBvdmVyIHRoZSBuYW1lZCBkYXRhc2V0cywgZ2V0IGEgbG9nZ2VkIGFuZCBhbiB1bmxvZ2dlZCB2ZXJzaW9uIG9mCiMgdGhlIGRhdGEsIHVzZSB0aGVzZSBhcyBpbnB1dHMgZm9yIHRoZSByZWdyZXNzaW9uCnJlc2lkdWFsX2FuYWx5c2lzX3Jlc3VsdHMgPSBsaXN0KCkKZm9yKG5uMSBpbiBuYW1lcyhtZXRhYm9sb21pY3NfcHJvY2Vzc2VkX2RhdGFzZXRzKSl7CiAgaWYoZ3JlcGwoInVudGFyZ2V0ZWQiLG5uMSkpe25leHR9CiAgeF9sb2cgPSBtZXRhYm9sb21pY3NfcHJvY2Vzc2VkX2RhdGFzZXRzW1tubjFdXSRzYW1wbGVfZGF0YQogIHhfdW5sb2cgPSAyXnhfbG9nCiAgCiAgIyB0YWtlIHRoZSBjb3ZhcmlhdGVzLCBpZ25vcmUgZGlzdGFuY2VzCiAgeF9tZXRhID0gdW5pcXVlKG1ldGFib2xvbWljc19wcm9jZXNzZWRfZGF0YXNldHNbW25uMV1dJHNhbXBsZV9tZXRhX3BhcnNlZCkKICBjdXJyX2NvdnMgPSB4X21ldGFbLGludGVyc2VjdChjb2xuYW1lcyh4X21ldGEpLGJpb3NwZWNfY29sc1syXSldCiAgY3Vycl9jb3ZzID0gZGF0YS5mcmFtZShjdXJyX2NvdnMsCiAgICAgICAgICAgc2V4PXhfbWV0YSRhbmltYWwucmVnaXN0cmF0aW9uLnNleCkKICAKICAjIGdldCB0aGUgbG0gb2JqZWN0cwogIGN1cnJfbW9kZWxzID0gbGlzdCgpCiAgZm9yKHRwIGluIHVuaXF1ZSh4X21ldGEkYW5pbWFsLmtleS50aW1lcG9pbnQpKXsKICAgICAgcmVzX2xvZyA9IGFwcGx5KAogICAgICAgIHhfbG9nLDEsCiAgICAgICAgcGFzczFhX3NpbXBsZV9kaWZmZXJlbnRpYWxfYWJ1bmRhbmNlLAogICAgICAgIHRwcyA9IHhfbWV0YSRhbmltYWwua2V5LnRpbWVwb2ludCx0cD10cCwKICAgICAgICBpc19jb250cm9sID0geF9tZXRhJGFuaW1hbC5rZXkuaXNfY29udHJvbCwKICAgICAgICBjb3ZzID0gY3Vycl9jb3ZzLHJldHVybl9tb2RlbD1UCiAgICAgICkKICAgICAgcmVzX3VubG9nID0gYXBwbHkoCiAgICAgICAgeF91bmxvZywxLAogICAgICAgIHBhc3MxYV9zaW1wbGVfZGlmZmVyZW50aWFsX2FidW5kYW5jZSwKICAgICAgICB0cHMgPSB4X21ldGEkYW5pbWFsLmtleS50aW1lcG9pbnQsdHA9dHAsCiAgICAgICAgaXNfY29udHJvbCA9IHhfbWV0YSRhbmltYWwua2V5LmlzX2NvbnRyb2wsCiAgICAgICAgY292cyA9IGN1cnJfY292cyxyZXR1cm5fbW9kZWw9VAogICAgICApCiAgICAgIGlzX25vcm0gPSBjYmluZCgKICAgICAgICBzYXBwbHkocmVzX2xvZyxmdW5jdGlvbih4KWlzX25vcm1hbF90ZXN0KHJlc2lkdWFscyh4KSkpLAogICAgICAgIHNhcHBseShyZXNfdW5sb2csZnVuY3Rpb24oeClpc19ub3JtYWxfdGVzdChyZXNpZHVhbHMoeCkpKQogICAgICApCiAgICAgIGNvbG5hbWVzKGlzX25vcm0pID0gYygibG9nIiwibm90IGxvZyIpCiAgICAgIGN1cnJfbW9kZWxzW1thcy5jaGFyYWN0ZXIodHApXV0gPSBpc19ub3JtCiAgfQogIHJlc2lkdWFsX2FuYWx5c2lzX3Jlc3VsdHNbW25uMV1dID0gY3Vycl9tb2RlbHMKfQoKIyBJcyB0aGVyZSBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgdHdvIG9wdGlvbnM/CmxvZ192c191bmxvZ19zdW1tX21hdCA9IHNhcHBseShyZXNpZHVhbF9hbmFseXNpc19yZXN1bHRzLAogICAgZnVuY3Rpb24oeClzYXBwbHkoeCwKICAgICAgICBmdW5jdGlvbih5KQogICAgICAgICAgd2lsY294LnRlc3QoeVssMV0seVssMl0scGFpcmVkID0gVCxhbHRlcm5hdGl2ZSA9ICJnIikkcC52YWx1ZSkpCgojIENvdW50IHRoZSBudW1iZXIgb2Ygbm9uLW5vcm1hbCBtZXRhYm9saXRlcwpudW1fbm9ubm9ybWFsX2xvZyA9IHNhcHBseShyZXNpZHVhbF9hbmFseXNpc19yZXN1bHRzLAogICAgZnVuY3Rpb24oeClzYXBwbHkoeCwKICAgICAgICBmdW5jdGlvbih5KXN1bSh5WywxXTwwLjA1KSkpCm51bV9ub25ub3JtYWxfbG9nID0gCiAgbnVtX25vbm5vcm1hbF9sb2dbLG9yZGVyKGNvbG5hbWVzKG51bV9ub25ub3JtYWxfbG9nKSldCm51bV9ub25ub3JtYWxfdW5sb2cgPSBzYXBwbHkocmVzaWR1YWxfYW5hbHlzaXNfcmVzdWx0cywKICAgIGZ1bmN0aW9uKHgpc2FwcGx5KHgsCiAgICAgICAgZnVuY3Rpb24oeSlzdW0oeVssMl08MC4wNSkpKQpudW1fbm9ubm9ybWFsX3VubG9nID0gCiAgbnVtX25vbm5vcm1hbF91bmxvZ1ssb3JkZXIoY29sbmFtZXMobnVtX25vbm5vcm1hbF91bmxvZykpXQoKbGlicmFyeShjb3JycGxvdCkKcGFyKG1hciA9IGMoNSw1LDUsMTApKQpjb3JycGxvdCh0KG51bV9ub25ub3JtYWxfbG9nKS0gdChudW1fbm9ubm9ybWFsX3VubG9nKSwKICAgICAgICAgaXMuY29yciA9IEYsdGwuY2V4ID0gMC43KQoKCmBgYAoKCiMgVHJhZ2V0ZWQgdnMuIFVudGFyZ2V0ZWQ6IHNpbmdsZSBtZXRhYm9saXRlIGNvbXBhcmlzb24KCiMjIENvbXB1dGUgc3RhdGlzdGljcyBmb3IgZWFjaCBkYXRhc2V0CgpDb21wYXJlIG92ZXJsYXBzLCBlZmZlY3Qgc2l6ZXMsIGFuZCBjb3JyZWxhdGlvbnMgd2l0aGluIHRpc3N1ZXMuIENvbXBhcmUgdGFyZ2V0ZWQtdW50YXJnZXRlZCBwYWlycyBvbmx5LiBGb3IgZGlmZmVyZW50aWFsIGFuYWx5c2lzIHdlIHVzZSB0aGUgc2FtZSBtb2RlbCBhcyBpbiB0aGUgYW5hbHlzaXMgYWJvdmUuCgpgYGB7cixvdXQuaGVpZ2h0PSc1MCUnLG91dC53aWR0aD0nNTAlJyxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9CgojIGhlbHBlciBmdW5jdGlvbiB0byB0cmFuc2Zvcm0gYSBtZXRhYm9sb21pY3MgbWF0cml4CiMgdG8gdGhhdCBvZiBpdHMgbW90cnBhYyBjb21wb3VuZCBpZHMKZXh0cmFjdF9tZXRhYl9kYXRhX2Zyb21fcm93X2Fubm90PC1mdW5jdGlvbih4LHJvd19hbm5vdF94KXsKICAjIGdldCB0aGUgY29sb3VtbiB0aGF0IGhhcyB0aGUgcm93IG5hbWVzCiAgaW50X3NpemVzID0gYXBwbHkocm93X2Fubm90X3gsMixmdW5jdGlvbih4LHkpbGVuZ3RoKGludGVyc2VjdCh4LHkpKSx5PXJvd25hbWVzKHgpKQogIGluZCA9IHdoaWNoKGludF9zaXplcz09bWF4KGludF9zaXplcyxuYS5ybSA9IFQpKVsxXQogIHJvd19hbm5vdF94ID0gcm93X2Fubm90X3hbaXMuZWxlbWVudChyb3dfYW5ub3RfeFssaW5kXSxzZXQ9cm93bmFtZXMoeCkpLF0KICByb3duYW1lcyhyb3dfYW5ub3RfeCkgPSByb3dfYW5ub3RfeFssaW5kXQogIHNoYXJlZCA9IGludGVyc2VjdChyb3duYW1lcyhyb3dfYW5ub3RfeCkscm93bmFtZXMoeCkpCiAgeCA9IHhbc2hhcmVkLF0KICByb3dfYW5ub3RfeCA9IHJvd19hbm5vdF94W3NoYXJlZCxdCiAgcm93bmFtZXMoeCkgPSByb3dfYW5ub3RfeCRtb3RycGFjX2NvbXBfbmFtZQogIHJldHVybih4KQp9CgpzaW5nbGVfbWV0YWJvbGl0ZV9jb3JycyA9IGxpc3QoKQpzaW5nbGVfbWV0YWJvbGl0ZV9kZSA9IGMoKQpuYW1lZDJjb3ZlcmVkX3NoYXJlZF9tZXRhYm9saXRlcyA9IGxpc3QoKQpmb3Iobm4xIGluIG5hbWVzKG1ldGFib2xvbWljc19wcm9jZXNzZWRfZGF0YXNldHMpKXsKICBubjFfdGlzc3VlID0gc3Ryc3BsaXQobm4xLHNwbGl0PSIsIilbWzFdXVsxXQogIG5uMV90aXNzdWUgPSBnc3ViKCJfcG93ZGVyIiwiIixubjFfdGlzc3VlKQogIGlmKGdyZXBsKCJ1bnRhcmdldGVkIixubjEpKXtuZXh0fQogIHNpbmdsZV9tZXRhYm9saXRlX2NvcnJzW1tubjFdXSA9IGxpc3QoKQogIG5hbWVkMmNvdmVyZWRfc2hhcmVkX21ldGFib2xpdGVzW1tubjFdXSA9IE5VTEwKICBmb3Iobm4yIGluIG5hbWVzKG1ldGFib2xvbWljc19wcm9jZXNzZWRfZGF0YXNldHMpKXsKICAgIGlmKG5uMiA9PSBubjEpe25leHR9CiAgICBpZighZ3JlcGwoInVudGFyZ2V0ZWQiLG5uMikpe25leHR9CiAgICBubjJfdGlzc3VlID0gc3Ryc3BsaXQobm4yLHNwbGl0PSIsIilbWzFdXVsxXQogICAgbm4yX3Rpc3N1ZSA9IGdzdWIoIl9wb3dkZXIiLCIiLG5uMl90aXNzdWUpCiAgICBubjJfZGF0YXNldCA9IHN0cnNwbGl0KG5uMixzcGxpdD0iLCIpW1sxXV1bMl0KICAgIGlmKG5uMV90aXNzdWUhPW5uMl90aXNzdWUpe25leHR9CiAgICAjIGdldCB0aGUgbnVtZXJpYyBkYXRhc2V0cyBhbmQgdGhlaXIgYW5ub3RhdGlvbgogICAgeCA9IG1ldGFib2xvbWljc19wcm9jZXNzZWRfZGF0YXNldHNbW25uMV1dJHNhbXBsZV9kYXRhCiAgICB5ID0gbWV0YWJvbG9taWNzX3Byb2Nlc3NlZF9kYXRhc2V0c1tbbm4yXV0kc2FtcGxlX2RhdGEKICAgIHJvd19hbm5vdF94ID0gbWV0YWJvbG9taWNzX3Byb2Nlc3NlZF9kYXRhc2V0c1tbbm4xXV0kcm93X2Fubm90CiAgICByb3dfYW5ub3RfeSA9IG1ldGFib2xvbWljc19wcm9jZXNzZWRfZGF0YXNldHNbW25uMl1dJHJvd19hbm5vdAogICAgIyB0cmFuc2Zvcm0gbWV0YWJvbGl0ZSBuYW1lcyB0byB0aGUgbW90cnBhYyBjb21wIG5hbWUKICAgIHggPSBleHRyYWN0X21ldGFiX2RhdGFfZnJvbV9yb3dfYW5ub3QoeCxyb3dfYW5ub3RfeCkKICAgIHkgPSBleHRyYWN0X21ldGFiX2RhdGFfZnJvbV9yb3dfYW5ub3QoeSxyb3dfYW5ub3RfeSkKICAgICMgYWxpZ24gdGhlIHNhbXBsZSBzZXRzCiAgICBiaWRfeSA9IG1lcmdlZF9kbWFxY19kYXRhW2NvbG5hbWVzKHkpLCJiaWQiXQogICAgYmlkX3ggPSBtZXJnZWRfZG1hcWNfZGF0YVtjb2xuYW1lcyh4KSwiYmlkIl0gICAgCiAgICAjIHN0ZXAgMTogbWVyZ2Ugc2FtcGxlcyBmcm9tIHRoZSBzYW1lIEJJRAogICAgaWYobGVuZ3RoKHVuaXF1ZShiaWRfeCkpIT1sZW5ndGgoYmlkX3gpKXsKICAgICAgeCA9IGFnZ3JlZ2F0ZV9yZXBlYXRlZF9zYW1wbGVzKHgsYmlkX3gpCiAgICB9CiAgICBlbHNlewogICAgICBjb2xuYW1lcyh4KSA9IGJpZF94CiAgICB9CiAgICBpZihsZW5ndGgodW5pcXVlKGJpZF95KSkhPWxlbmd0aChiaWRfeSkpewogICAgICB5ID0gYWdncmVnYXRlX3JlcGVhdGVkX3NhbXBsZXMoeSxiaWRfeSkKICAgIH1lbHNlewogICAgICBjb2xuYW1lcyh5KSA9IGJpZF95CiAgICB9CiAgICAjIHN0ZXAgMjogdXNlIHRoZSBzaGFyZWQgYmlvIGlkcwogICAgc2hhcmVkX2JpZHMgPSBhcy5jaGFyYWN0ZXIoaW50ZXJzZWN0KGNvbG5hbWVzKHkpLGNvbG5hbWVzKHgpKSkKICAgIHggPSBhcy5tYXRyaXgoeFssc2hhcmVkX2JpZHNdKQogICAgeSA9IGFzLm1hdHJpeCh5WyxzaGFyZWRfYmlkc10pCiAgICAjIEF0IHRoaXMgcG9pbnQgeCBhbmQgeSBhcmUgb3ZlciB0aGUgc2FtZSBCSURzLCBub3cgd2UgYWRkIHRoZSBtZXRhZGF0YQogICAgeV9tZXRhID0gdW5pcXVlKG1ldGFib2xvbWljc19wcm9jZXNzZWRfZGF0YXNldHNbW25uMV1dJHNhbXBsZV9tZXRhX3BhcnNlZCkKICAgIHJvd25hbWVzKHlfbWV0YSkgPSB5X21ldGEkYmlkCiAgICB5X21ldGEgPSB5X21ldGFbc2hhcmVkX2JpZHMsXQogICAgCiAgICAjIGdldCB0aGUgc2hhcmVkIG1hdGVib2xpdGVzCiAgICBzaGFyZWRfbWV0YWJvbGl0ZXMgPSBpbnRlcnNlY3Qocm93bmFtZXMoeCkscm93bmFtZXMoeSkpCiAgICBzaGFyZWRfbWV0YWJvbGl0ZXMgPSBuYS5vbWl0KHNoYXJlZF9tZXRhYm9saXRlcykKICAgIGlmKGxlbmd0aChzaGFyZWRfbWV0YWJvbGl0ZXMpPT0wKXtuZXh0fQogICAgbmFtZWQyY292ZXJlZF9zaGFyZWRfbWV0YWJvbGl0ZXNbW25uMV1dID0gdW5pb24oCiAgICAgIG5hbWVkMmNvdmVyZWRfc2hhcmVkX21ldGFib2xpdGVzW1tubjFdXSwKICAgICAgc2hhcmVkX21ldGFib2xpdGVzCiAgICApCiAgICAKICAgICMgQ29tcHV0ZSB0aGUgY29ycmVsYXRpb24gbWF0cmljZXMgb2YgdGhlIHNoYXJlZCBtZXRhYm9saXRlcwogICAgaWYobGVuZ3RoKHNoYXJlZF9tZXRhYm9saXRlcyk+MSl7CiAgICAgICAgICBjb3JycyA9Y29yKHQoeFtzaGFyZWRfbWV0YWJvbGl0ZXMsXSksCiAgICAgICAgICAgICAgICB0KHlbc2hhcmVkX21ldGFib2xpdGVzLF0pLG1ldGhvZCA9ICJzcGVhcm1hbiIpCiAgICB9CiAgICBlbHNlewogICAgICAgICAgY29ycnMgPSBjb3IoeFtzaGFyZWRfbWV0YWJvbGl0ZXMsXSwKICAgICAgICAgICAgICAgIHlbc2hhcmVkX21ldGFib2xpdGVzLF0sbWV0aG9kID0gInNwZWFybWFuIikKICAgIH0KICAgIAogICAgIyB0YWtlIHRoZSBjb3ZhcmlhdGVzIChpZ25vcmUgZGlzdGFuY2VzKQogICAgY3Vycl9jb3ZfY29scyA9IGludGVyc2VjdChjb2xuYW1lcyh5X21ldGEpLGJpb3NwZWNfY29sc1syXSkKICAgIGN1cnJfY292cyA9IGRhdGEuZnJhbWUoeV9tZXRhWyxjdXJyX2Nvdl9jb2xzXSkKICAgIG5hbWVzKGN1cnJfY292cykgPSBjdXJyX2Nvdl9jb2xzCiAgICBjdXJyX2NvdnMkc2V4ID0geV9tZXRhJGFuaW1hbC5yZWdpc3RyYXRpb24uc2V4ICMgYWRkIHNleAogICAgCiAgICAjIGRpZmZlcmVudGlhbCBhbmFseXNpcwogICAgZm9yKHRwIGluIHVuaXF1ZSh5X21ldGEkYW5pbWFsLmtleS50aW1lcG9pbnQpKXsKICAgICAgcmVzeCA9IHQoYXBwbHkoCiAgICAgICAgbWF0cml4KHhbc2hhcmVkX21ldGFib2xpdGVzLF0sbnJvdz1sZW5ndGgoc2hhcmVkX21ldGFib2xpdGVzKSksMSwKICAgICAgICBwYXNzMWFfc2ltcGxlX2RpZmZlcmVudGlhbF9hYnVuZGFuY2UsCiAgICAgICAgdHBzID0geV9tZXRhJGFuaW1hbC5rZXkudGltZXBvaW50LHRwPXRwLAogICAgICAgIGlzX2NvbnRyb2wgPSB5X21ldGEkYW5pbWFsLmtleS5pc19jb250cm9sLAogICAgICAgIGNvdnMgPSBjdXJyX2NvdnMscmV0dXJuX21vZGVsPUYKICAgICAgKSkKICAgICAgcmVzeSA9IHQoYXBwbHkoCiAgICAgICAgbWF0cml4KHlbc2hhcmVkX21ldGFib2xpdGVzLF0sbnJvdz1sZW5ndGgoc2hhcmVkX21ldGFib2xpdGVzKSksMSwKICAgICAgICBwYXNzMWFfc2ltcGxlX2RpZmZlcmVudGlhbF9hYnVuZGFuY2UsCiAgICAgICAgdHBzID0geV9tZXRhJGFuaW1hbC5rZXkudGltZXBvaW50LHRwPXRwLAogICAgICAgIGlzX2NvbnRyb2wgPSB5X21ldGEkYW5pbWFsLmtleS5pc19jb250cm9sLAogICAgICAgIGNvdnMgPSBjdXJyX2NvdnMscmV0dXJuX21vZGVsPUYKICAgICAgKSkKICAgICAgIyBBZGQgZGF0YXNldCBpbmZvcm1hdGlvbiwgdGltZSBwb2ludCwgdGlzc3VlCiAgICAgICMgVGhlc2UgYXJlIGltcG9ydGFudCBhbm5vdGF0aW9ucyBmb3Igb3VyIHN1bW1hcnkgbWF0cml4CiAgICAgICMgY2FsbGVkIHNpbmdsZV9tZXRhYm9saXRlX2RlIGJlbG93CiAgICAgIGFkZGVkX2NvbHVtbnMgPSBtYXRyaXgoY2JpbmQoCiAgICAgICAgcmVwKG5uMSxsZW5ndGgoc2hhcmVkX21ldGFib2xpdGVzKSksCiAgICAgICAgcmVwKG5uMixsZW5ndGgoc2hhcmVkX21ldGFib2xpdGVzKSksCiAgICAgICAgc2hhcmVkX21ldGFib2xpdGVzLAogICAgICAgIHJlcCh0cCxsZW5ndGgoc2hhcmVkX21ldGFib2xpdGVzKSksCiAgICAgICAgcmVwKG5uMV90aXNzdWUsbGVuZ3RoKHNoYXJlZF9tZXRhYm9saXRlcykpCiAgICAgICksbnJvdz1sZW5ndGgoc2hhcmVkX21ldGFib2xpdGVzKSkKICAgICAgcmVzeCA9IGNiaW5kKHJlc3gscmVwKFQsbnJvdyhyZXN4KSkpCiAgICAgIGNvbG5hbWVzKHJlc3gpW25jb2wocmVzeCldID0gImlzX3RhcmdldGVkIgogICAgICByZXN5ID0gY2JpbmQocmVzeSxyZXAoRixucm93KHJlc3kpKSkKICAgICAgY29sbmFtZXMocmVzeSlbbmNvbChyZXN5KV0gPSAiaXNfdGFyZ2V0ZWQiCiAgICAgIGlmKG5yb3cocmVzeCk+MSl7CiAgICAgICAgcmVzeCA9IGNiaW5kKGFkZGVkX2NvbHVtbnNbLC0yXSxyZXN4KQogICAgICAgIHJlc3kgPSBjYmluZChhZGRlZF9jb2x1bW5zWywtMV0scmVzeSkKICAgICAgfQogICAgICBlbHNlewogICAgICAgIHJlc3ggPSBjKGFkZGVkX2NvbHVtbnNbLC0yXSxyZXN4KQogICAgICAgIHJlc3kgPSBjKGFkZGVkX2NvbHVtbnNbLC0xXSxyZXN5KQogICAgICB9CiAgICAgIHNpbmdsZV9tZXRhYm9saXRlX2RlID0gcmJpbmQoc2luZ2xlX21ldGFib2xpdGVfZGUscmVzeCkKICAgICAgc2luZ2xlX21ldGFib2xpdGVfZGUgPSByYmluZChzaW5nbGVfbWV0YWJvbGl0ZV9kZSxyZXN5KQogICAgfQogICAgCiAgICBzaW5nbGVfbWV0YWJvbGl0ZV9jb3Jyc1tbbm4xXV1bW25uMl1dID0gY29ycnMKICB9Cn0KCiMgUmVmb3JtYXQgdGhlIHJlc3VsdHMgZm9yIGFuIGVhc2llciBjb21wYXJpc29uIGxhdGVyCnNpbmdsZV9tZXRhYm9saXRlX2RlID0gZGF0YS5mcmFtZShzaW5nbGVfbWV0YWJvbGl0ZV9kZSkKbmFtZXMoc2luZ2xlX21ldGFib2xpdGVfZGUpID0gYygKICAiZGF0YXNldCIsIm1ldGFib2xpdGUiLCJ0cCIsInRpc3N1ZSIsCiAgIkVzdCIsIlN0ZCIsIlRzdGF0IiwiUHZhbHVlIiwiaXNfdGFyZ2V0ZWQiKQpmb3IoY29sIGluIG5hbWVzKHNpbmdsZV9tZXRhYm9saXRlX2RlKVstYygxOjQpXSl7CiAgc2luZ2xlX21ldGFib2xpdGVfZGVbW2NvbF1dID0gYXMubnVtZXJpYygKICAgIGFzLmNoYXJhY3RlcihzaW5nbGVfbWV0YWJvbGl0ZV9kZVtbY29sXV0pKQp9CmZvcihjb2wgaW4gbmFtZXMoc2luZ2xlX21ldGFib2xpdGVfZGUpWzE6NF0pewogIHNpbmdsZV9tZXRhYm9saXRlX2RlW1tjb2xdXSA9IAogICAgYXMuY2hhcmFjdGVyKHNpbmdsZV9tZXRhYm9saXRlX2RlW1tjb2xdXSkKfQojIFJlbW92ZSBkdXBsaWNhdGlvbnMKcm93bmFtZXMoc2luZ2xlX21ldGFib2xpdGVfZGUpID0gTlVMTApmb3Iobm4gaW4gbmFtZXMoc2luZ2xlX21ldGFib2xpdGVfZGUpKXsKICBuZGlnID0gNQogIGlmKGdyZXBsKCJwdmFsIixubixpZ25vcmUuY2FzZSA9IFQpKXsKICAgIG5kaWcgPSAxMAogIH0KICBpZihpcy5udW1lcmljKHNpbmdsZV9tZXRhYm9saXRlX2RlW1tubl1dKSl7CiAgICBzaW5nbGVfbWV0YWJvbGl0ZV9kZVtbbm5dXSA9IHJvdW5kKHNpbmdsZV9tZXRhYm9saXRlX2RlW1tubl1dLGRpZ2l0cyA9IG5kaWcpCiAgfQp9CnNpbmdsZV9tZXRhYm9saXRlX2RlID0gdW5pcXVlKHNpbmdsZV9tZXRhYm9saXRlX2RlKQoKIyBhIGhlbHBlciBmdW5jdGlvbiBmb3Igc2ltcGxpZnlpbmcgZGF0YXNldCBuYW1lcwpzaW1wbGlmeV9tZXRhYl9kYXRhc2V0X25hbWU8LWZ1bmN0aW9uKHMpewogIHMgPSBnc3ViKCJtZXRhYl91XyIsIiIscykKICBzID0gZ3N1YigiLHVudGFyZ2V0ZWQiLCIiLHMpCiAgcyA9IGdzdWIoIixuYW1lZCIsIiIscykKICByZXR1cm4ocykKfQpzaW5nbGVfbWV0YWJvbGl0ZV9kZVssMV0gPSBzYXBwbHkoc2luZ2xlX21ldGFib2xpdGVfZGVbLDFdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2ltcGxpZnlfbWV0YWJfZGF0YXNldF9uYW1lKQoKYGBgCgpXZSBuZXh0IHRyYW5zZm9ybSB0aGUgZGF0YSBhYm92ZSBpbnRvIHRhYmxlcyB0aGF0IGNvbnRhaW4gZGF0YSBmb3IgZWFjaCBjb21iaW5hdGlvbiBvZiBtZXRhYm9saXRlLCB0aW1lIHBvaW50LCBhbmQgdGlzc3VlLiBUaGVzZSBhcmUgdGhlbiB1c2VkIGZvciBkaWZmZXJlbnQgbWV0YS1hbmFseXNlczogKDEpIGEgc2ltcGxlIHJhbmRvbSBlZmZlY3RzIGFuYWx5c2lzLCAoMikgcmFuZG9tIGVmZmVjdHMgd2l0aCBhIGJpbmFyeSBjb3ZhcmlhdGUgaW5kaWNhdGluZyBpZiBhIGRhdGFzZXQgaXMgdGFyZ2V0ZWQgb3IgdW50YXJnZXRlZCwgKDMpIHJlZG8gdGhlIFJFIG1vZGVsIG9mICgxKSB3aXRoIHRoZSB0YXJnZXRlZCBkYXRhIG9ubHksIGFuZCAoNCkgcmVkbyB0aGUgUkUgbW9kZWwgb2YgKDEpIHdpdGggdGhlIHVudGFyZ2V0ZWQgZGF0YSBvbmx5LgoKYGBge3Isb3V0LmhlaWdodD0nNTAlJyxvdXQud2lkdGg9JzUwJScsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQoKbGlicmFyeShtZXRhZm9yKQptZXRhX2FuYWx5c2lzX3N0YXRzID0gbGlzdCgpCmZvcih0aXNzdWUgaW4gdW5pcXVlKHNpbmdsZV9tZXRhYm9saXRlX2RlJHRpc3N1ZSkpewogIGZvcih0cCBpbiB1bmlxdWUoc2luZ2xlX21ldGFib2xpdGVfZGUkdHApKXsKICAgIGN1cnJfc3Vic2V0ID0gc2luZ2xlX21ldGFib2xpdGVfZGVbCiAgICAgIHNpbmdsZV9tZXRhYm9saXRlX2RlJHRpc3N1ZT09dGlzc3VlICYKICAgICAgICBzaW5nbGVfbWV0YWJvbGl0ZV9kZSR0cD09dHAsXQogICAgZm9yKG1ldGFib2xpdGUgaW4gdW5pcXVlKGN1cnJfc3Vic2V0JG1ldGFib2xpdGUpKXsKICAgICAgY3Vycl9tZXRfZGF0YSA9IGN1cnJfc3Vic2V0WwogICAgICAgIGN1cnJfc3Vic2V0JG1ldGFib2xpdGU9PW1ldGFib2xpdGUsXQogICAgICBjdXJyX21ldF9kYXRhJHZhciA9IGN1cnJfbWV0X2RhdGEkU3RkXjIKICAgICAgcmVfbW9kZWwxID0gTlVMTDtyZV9tb2RlbDI9TlVMTAogICAgICByZV9tb2RlbF90YXIgPSBOVUxMO3JlX21vZGVsX3VudGFyID0gTlVMTAogICAgICB0cnkoe3JlX21vZGVsMSA9IHJtYS51bmkoY3Vycl9tZXRfZGF0YSRFc3QsY3Vycl9tZXRfZGF0YSR2YXIpfSkKICAgICAgdHJ5KHtyZV9tb2RlbDIgPSBybWEubXYoY3Vycl9tZXRfZGF0YSRFc3QsY3Vycl9tZXRfZGF0YSR2YXIsCiAgICAgICAgICAgICAgICAgICAgICAgICBtb2RzPWN1cnJfbWV0X2RhdGEkaXNfdGFyZ2V0ZWQpfSkKICAgICAgdHJ5KHtyZV9tb2RlbF90YXIgPSBybWEudW5pKAogICAgICAgIGN1cnJfbWV0X2RhdGFbY3Vycl9tZXRfZGF0YSRpc190YXJnZXRlZD09MSwiRXN0Il0sCiAgICAgICAgY3Vycl9tZXRfZGF0YVtjdXJyX21ldF9kYXRhJGlzX3RhcmdldGVkPT0xLCJ2YXIiXQogICAgICApfSkKICAgICAgdHJ5KHtyZV9tb2RlbF91bnRhciA9IHJtYS51bmkoCiAgICAgICAgY3Vycl9tZXRfZGF0YVtjdXJyX21ldF9kYXRhJGlzX3RhcmdldGVkPT0wLCJFc3QiXSwKICAgICAgICBjdXJyX21ldF9kYXRhW2N1cnJfbWV0X2RhdGEkaXNfdGFyZ2V0ZWQ9PTAsInZhciJdCiAgICAgICl9KQogICAgICBtZXRhX2FuYWx5c2lzX3N0YXRzW1twYXN0ZShtZXRhYm9saXRlLHRpc3N1ZSx0cCxzZXA9IiwiKV1dID0gCiAgICAgICAgbGlzdChjdXJyX21ldF9kYXRhPWN1cnJfbWV0X2RhdGEscmVfbW9kZWwxPXJlX21vZGVsMSwKICAgICAgICAgICAgcmVfbW9kZWwyID0gcmVfbW9kZWwyLHJlX21vZGVsX3Rhcj1yZV9tb2RlbF90YXIsCiAgICAgICAgICAgIHJlX21vZGVsX3VudGFyID0gcmVfbW9kZWxfdW50YXIpCiAgICB9CiAgfQp9CgpgYGAKCiMjIENvbXBhcmlzb24gcmVzdWx0cwoKV2Ugbm93IHNob3cgc29tZSBwbG90cyB0byBzdW1tYXJpemUgdGhlIGNvbXBhcmlzb24uIAoKIyMjIERhdGFzZXQgY292ZXJhZ2UgCgpXZSBmaXJzdCBwbG90IHRoZSBudW1iZXIgYW5kIHBlcmNlbnRhZ2Ugb2YgbWV0YWJvbGl0ZXMgaW4gdGhlIHRhcmdldGVkIGRhdGFzZXRzIHRoYXQgYXJlIG1lYXN1cmVkIGluIGF0IGxlYXN0IG9uZSB1bnRhcmdldGVkIGRhdGFzZXQuCgpgYGB7cixvdXQuaGVpZ2h0PSc1MCUnLG91dC53aWR0aD0nNTAlJyxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZ2dwbG90MikKZGF0YXNldDJudW1fbWV0YWJvbGl0ZXMgPSBzYXBwbHkobWV0YWJvbG9taWNzX3Byb2Nlc3NlZF9kYXRhc2V0cywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpbnJvdyh4JHNhbXBsZV9kYXRhKSkKbmFtZWRfZGF0YXNldF9jb3ZlcmFnZSA9IHNhcHBseShuYW1lZDJjb3ZlcmVkX3NoYXJlZF9tZXRhYm9saXRlcyxsZW5ndGgpCm5hbWVkX2RhdGFzZXRfY292ZXJhZ2UgPSBkYXRhLmZyYW1lKAogIG5hbWUgPSBuYW1lcyhuYW1lZF9kYXRhc2V0X2NvdmVyYWdlKSwKICBwZXJjZW50YWdlID0gbmFtZWRfZGF0YXNldF9jb3ZlcmFnZSAvCiAgZGF0YXNldDJudW1fbWV0YWJvbGl0ZXNbbmFtZXMobmFtZWRfZGF0YXNldF9jb3ZlcmFnZSldLAogIGNvdW50ID0gbmFtZWRfZGF0YXNldF9jb3ZlcmFnZSwKICB0b3RhbCA9IGRhdGFzZXQybnVtX21ldGFib2xpdGVzW25hbWVzKG5hbWVkX2RhdGFzZXRfY292ZXJhZ2UpXQopCiMgYWRkIGRhdGFzZXRzIHdpdGggbm8gY292ZXJhZ2UKYWxsX3RhcmdldGVkX2RhdGFzZXRzID0gbmFtZXMobWV0YWJvbG9taWNzX3Byb2Nlc3NlZF9kYXRhc2V0cykKYWxsX3RhcmdldGVkX2RhdGFzZXRzID0gYWxsX3RhcmdldGVkX2RhdGFzZXRzWyFncmVwbCgidW50YXJnIixhbGxfdGFyZ2V0ZWRfZGF0YXNldHMpXQp6ZXJvX2NvdmVyYWdlX2RhdGFzZXRzID0gc2V0ZGlmZihhbGxfdGFyZ2V0ZWRfZGF0YXNldHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVkX2RhdGFzZXRfY292ZXJhZ2UkbmFtZSkKemVyb19jb3ZlcmFnZV9kYXRhc2V0cyA9IGRhdGEuZnJhbWUoCiAgbmFtZSA9IHplcm9fY292ZXJhZ2VfZGF0YXNldHMsCiAgcGVyY2VudGFnZSA9IHJlcCgwLGxlbmd0aCh6ZXJvX2NvdmVyYWdlX2RhdGFzZXRzKSksCiAgY291bnQgPSByZXAoMCxsZW5ndGgoemVyb19jb3ZlcmFnZV9kYXRhc2V0cykpLAogIHRvdGFsID0gZGF0YXNldDJudW1fbWV0YWJvbGl0ZXNbemVyb19jb3ZlcmFnZV9kYXRhc2V0c10KKQpuYW1lZF9kYXRhc2V0X2NvdmVyYWdlID0gcmJpbmQobmFtZWRfZGF0YXNldF9jb3ZlcmFnZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgemVyb19jb3ZlcmFnZV9kYXRhc2V0cykKbmFtZWRfZGF0YXNldF9jb3ZlcmFnZSA9IAogIG5hbWVkX2RhdGFzZXRfY292ZXJhZ2Vbb3JkZXIoYXMuY2hhcmFjdGVyKG5hbWVkX2RhdGFzZXRfY292ZXJhZ2UkbmFtZSkpLF0KcHJpbnQoZ2dwbG90KG5hbWVkX2RhdGFzZXRfY292ZXJhZ2UsIGFlcyh4PW5hbWUsIHk9cGVyY2VudGFnZSkpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsd2lkdGg9MC4yKSArIGNvb3JkX2ZsaXAoKSArCiAgZ2VvbV90ZXh0KGRhdGE9bmFtZWRfZGF0YXNldF9jb3ZlcmFnZSwgCiAgICAgICAgICAgIGFlcyhuYW1lLCBwZXJjZW50YWdlKzAuMDUsIGxhYmVsPWNvdW50KSwgCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGg9MC45KSwKICAgICAgICAgICAgc2l6ZT00KSArIAogIGdndGl0bGUoIlRhcmdldGVkIGRhdGFzZXQ6IGNvdmVyYWdlIGJ5IHVudGFyZ2V0ZWQiKSkKCmBgYAoKIyMjIFNwZWFybWFuIGNvcnJlbGF0aW9ucwoKV2UgZXhhbWluZSB0aGUgYXZlcmFnZSBhYnNvbHV0ZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBwbGF0Zm9ybXMgKHdpdGhpbiB0aXNzdWVzKS4gV2hlbmV2ZXIgdHdvIHBsYXRmb3JtcyBzaGFyZSBtb3JlIHRoYW4gYSBzaW5nbGUgbWV0YWJvbGl0ZSB3ZSBwbG90IGJvdGggdGhlIGF2ZXJhZ2UgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgc2FtZSBtZXRhYm9saXRlcyBhbmQgYmV0d2VlbiBvdGhlciBtZXRhYm9saXRlcy4gQWRkaW5nIHRoZSBhdmVyYWdlIGNvcnJlbGF0aW9uIGJldHdlZW4gcGxhdGZvcm1zIGJ1dCB3aXRoIGRpZmZlcmVudCBtZXRhYm9saXRlcyBpcyBpbXBvcnRhbnQgYXMgaXQgZ2l2ZXMgc29tZSBwZXJzcGVjdGl2ZSB0byB3aGF0IGEgc2lnbmlmaWNhbnQgY29ycmVsYXRpb24gaXMuIFRoYXQgaXMsIGluIG1hbnkgY2FzZXMgYmVsb3csIHRoZSBhdmVyYWdlIGNvcnJlbGF0aW9uIG1heSBiZSBncmVhdGVyIHRoYW4gZXhwZWN0ZWQuCgpgYGB7cixvdXQuaGVpZ2h0PSc1MCUnLG91dC53aWR0aD0nNTAlJyxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9CiMgTmV4dCBleGFtaW5lIHRoZSBTcGVhcm1hbiBjb3JyZWxhdGlvbnMgYmV0d2VlbiBwbGF0Zm9ybXMKbWVhbl9hYnM8LWZ1bmN0aW9uKHgsLi4uKXtyZXR1cm4obWVhbihhYnMoeCksLi4uKSl9CnNkX2FiczwtZnVuY3Rpb24oeCwuLi4pe3JldHVybihzZChhYnMoeCksLi4uKSl9CmV4dHJhY3RfZGlhZ192c19ub25fZGlhZzwtZnVuY3Rpb24oY29ycnMsZnVuYz1tZWFuX2FicywuLi4pewogIGlmKGxlbmd0aChjb3Jycyk9PTEpewogICAgcmV0dXJuKGMoc2FtZT1mdW5jKGNvcnJzLC4uLiksb3RoZXI9TkEpKQogIH0KICBzYW1lID0gZnVuYyhkaWFnKGNvcnJzKSwuLi4pCiAgb3RoZXIgPSBmdW5jKAogICAgYyhjb3Jyc1tsb3dlci50cmkoY29ycnMsZGlhZyA9IEYpXSksLi4uKQogIHJldHVybihjKHNhbWU9c2FtZSxvdGhlcj1vdGhlcikpCn0KCmZvcih0YXJfZGF0YXNldCBpbiBuYW1lcyhzaW5nbGVfbWV0YWJvbGl0ZV9jb3JycykpewogIGwgPSBzaW5nbGVfbWV0YWJvbGl0ZV9jb3Jyc1tbdGFyX2RhdGFzZXRdXQogIGlmKGxlbmd0aChsKT09MCl7bmV4dH0KICBjb3JyX2luZm8gPSBhcy5kYXRhLmZyYW1lKHQoc2FwcGx5KGwsIGV4dHJhY3RfZGlhZ192c19ub25fZGlhZykpKQogIGNvcnJfc2QgPSBhcy5kYXRhLmZyYW1lKHQoc2FwcGx5KGwsIGV4dHJhY3RfZGlhZ192c19ub25fZGlhZyxmdW5jPXNkX2FicykpKQogIHJvd25hbWVzKGNvcnJfaW5mbykgPSBzYXBwbHkocm93bmFtZXMoY29ycl9pbmZvKSwKICAgICAgZnVuY3Rpb24oeClzdHJzcGxpdCh4LHNwbGl0PSIsIilbWzFdXVsyXSkKICByb3duYW1lcyhjb3JyX2luZm8pID0gZ3N1YigibWV0YWJfdV8iLCIiLHJvd25hbWVzKGNvcnJfaW5mbykpCiAgcm93bmFtZXMoY29ycl9zZCkgPSByb3duYW1lcyhjb3JyX2luZm8pCiAgY29ycl9pbmZvJGRhdGFzZXQgPSByb3duYW1lcyhjb3JyX2luZm8pCiAgY29ycl9zZCRkYXRhc2V0ID0gY29ycl9pbmZvJGRhdGFzZXQKICBjb3JyX2luZm8gPSBtZWx0KGNvcnJfaW5mbykKICBjb3JyX3NkID0gbWVsdChjb3JyX3NkKQogIGNvcnJfaW5mbyRzZCA9IGNvcnJfc2QkdmFsdWUKICBwcmludCgKICAgIGdncGxvdChjb3JyX2luZm8sIGFlcyh4PWRhdGFzZXQsIHk9dmFsdWUsIGZpbGw9dmFyaWFibGUpKSArCiAgICAgIGdlb21fYmFyKHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKCksIHN0YXQ9ImlkZW50aXR5IiwgY29sb3VyPSdibGFjaycpICsKICAgICAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj12YWx1ZS1zZCwgeW1heD12YWx1ZStzZCksbmEucm09VCwgCiAgICAgICAgICAgICAgICAgICB3aWR0aD0uMixwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSguOSkpICsKICAgIGdndGl0bGUodGFyX2RhdGFzZXQpICsgeGxhYigiVW50YXJnZXRlZCBkYXRhc2V0IikgKyB5bGFiKCJTcGVhcm1hbiIpICsKICAgICAgbGFicyhmaWxsID0gIlBhaXIgdHlwZSIpICsgCiAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0idG9wIixsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiKQogICkKfQpgYGAKCiMjIyBNZXRhLWFuYWx5c2lzIG9mIGRpZmZlcmVudGlhbCBlZmZlY3RzCgpIZXJlIHdlIGdvIGludG8gdGhlIHNpbmdsZSBtZXRhYm9saXRlcyBjb21wYXJpc29uIGluIGdyZWF0ZXIgZGV0YWlsIChhZ2Fpbiwgd2l0aGluIHRpc3N1ZXMpLiAKCldlIHN0YXJ0IHdpdGggYSBmZXcgZXhhbXBsZXMuIEhlcmUgYXJlIHRoZSByZXN1bHRzIGZvciBsYWN0YXRlIGluIHBsYXNtYS4KCmBgYHtyLG91dC5oZWlnaHQ9JzUwJScsb3V0LndpZHRoPSc1MCUnLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0KbGFjdF9yZXMgPSBtZXRhX2FuYWx5c2lzX3N0YXRzWwogIGdyZXBsKCJsYWN0IixuYW1lcyhtZXRhX2FuYWx5c2lzX3N0YXRzKSxpZ25vcmUuY2FzZSA9IFQpICYKICAgIGdyZXBsKCJwbGFzbWEiLG5hbWVzKG1ldGFfYW5hbHlzaXNfc3RhdHMpLGlnbm9yZS5jYXNlID0gVCkKXQpsYWN0X3Jlc19ob3VycyA9IHNhcHBseShuYW1lcyhsYWN0X3JlcyksCiAgICAgICAgICAgIGZ1bmN0aW9uKHgpYXMubnVtZXJpYyhzdHJzcGxpdCh4LHNwbGl0PSIsIilbWzFdXVszXSkpCmxhY3RfcmVzID0gbGFjdF9yZXNbb3JkZXIobGFjdF9yZXNfaG91cnMpXQpmb3IobGFjdF9leGFtcGxlIGluIG5hbWVzKGxhY3RfcmVzKVsxOjZdKXsKICBjdXJyX2xhYmVscyA9IGdzdWIoInBsYXNtYSwiLCIiLAogICAgICAgICAgICAgICAgICAgICBtZXRhX2FuYWx5c2lzX3N0YXRzW1tsYWN0X2V4YW1wbGVdXVtbMV1dWywxXSkKICBmb3Jlc3QobWV0YV9hbmFseXNpc19zdGF0c1tbbGFjdF9leGFtcGxlXV0kcmVfbW9kZWwxLAogICAgICAgc2xhYiA9IGN1cnJfbGFiZWxzLAogICAgICAgbWFpbiA9IGxhY3RfZXhhbXBsZSx4bGFiID0gIkxvZyBmYyIsCiAgICAgICBjb2wgPSAiYmx1ZSIsY2V4ID0gMS4xKQp9CgpgYGAKCldlIGNhbiBub3cgY2hlY2sgdGhlIHNhbWUgYW5hbHlzaXMgZm9yIGxpdmVyOgoKYGBge3Isb3V0LmhlaWdodD0nNTAlJyxvdXQud2lkdGg9JzUwJScsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQpsYWN0X3JlcyA9IG1ldGFfYW5hbHlzaXNfc3RhdHNbCiAgZ3JlcGwoImxhY3QiLG5hbWVzKG1ldGFfYW5hbHlzaXNfc3RhdHMpLGlnbm9yZS5jYXNlID0gVCkgJgogICAgZ3JlcGwoImxpdmVyIixuYW1lcyhtZXRhX2FuYWx5c2lzX3N0YXRzKSxpZ25vcmUuY2FzZSA9IFQpCl0KbGFjdF9yZXNfaG91cnMgPSBzYXBwbHkobmFtZXMobGFjdF9yZXMpLAogICAgICAgICAgICBmdW5jdGlvbih4KWFzLm51bWVyaWMoc3Ryc3BsaXQoeCxzcGxpdD0iLCIpW1sxXV1bM10pKQpsYWN0X3JlcyA9IGxhY3RfcmVzW29yZGVyKGxhY3RfcmVzX2hvdXJzKV0KZm9yKGxhY3RfZXhhbXBsZSBpbiBuYW1lcyhsYWN0X3JlcylbMTo2XSl7CiAgY3Vycl9sYWJlbHMgPSBnc3ViKCJsaXZlcl9wb3dkZXIsIiwiIiwKICAgICAgICAgICAgICAgICAgICAgbWV0YV9hbmFseXNpc19zdGF0c1tbbGFjdF9leGFtcGxlXV1bWzFdXVssMV0pCiAgZm9yZXN0KG1ldGFfYW5hbHlzaXNfc3RhdHNbW2xhY3RfZXhhbXBsZV1dJHJlX21vZGVsMSwKICAgICAgIHNsYWIgPSBjdXJyX2xhYmVscywKICAgICAgIG1haW4gPSBsYWN0X2V4YW1wbGUseGxhYiA9ICJMb2cgZmMiLAogICAgICAgY29sID0gImJsdWUiLGNleCA9IDEuMSkKfQoKYGBgCgpXZSBub3cgbW92ZSB0byBhIHN5c3RlbWF0aWMgY29tcGFyaXNvbiBvZiB0aGUgZGF0YXNldHMuIFdlIHN0YXJ0IHdpdGggYSBuYWl2ZSBjb21wYXJpc29uLCBjaGVja2luZyBpZiBtZXRhYm9saXRlcyBhcmUgY29uc2lzdGVudGx5IGJlaW5nIGRpc2NvdmVyZWQgdXNpbmcgYSBzaW1wbGUgJHA9MC4wMDEkIHRocmVzaG9sZCBmb3Igc2lnbmlmaWNhbmNlLgoKYGBge3Isb3V0LmhlaWdodD0nNTAlJyxvdXQud2lkdGg9JzUwJScsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQojIE5haXZlIGNvbXBhcmlzb24KdGhyID0gMC4wMDEKbmFpdmVfYW5hbHlzaXNfdGFibGVzID0gbGFwcGx5KG1ldGFfYW5hbHlzaXNfc3RhdHMsCiAgICBmdW5jdGlvbih4KXRhYmxlKHgkY3Vycl9tZXRfZGF0YVssIlB2YWx1ZSJdPHRociwKICAgICAgICAgICAgICAgICAgICAgeCRjdXJyX21ldF9kYXRhWywiaXNfdGFyZ2V0ZWQiXSkpCnRhYmxlX3dpdGhfc2lnX3Jlc3VsdHMgPSBuYWl2ZV9hbmFseXNpc190YWJsZXNbCiAgc2FwcGx5KG5haXZlX2FuYWx5c2lzX3RhYmxlcyxucm93KT4xCl0KcGVyY2VudF93aXRoX3NpZyA9IAogIGxlbmd0aCh0YWJsZV93aXRoX3NpZ19yZXN1bHRzKS9sZW5ndGgobmFpdmVfYW5hbHlzaXNfdGFibGVzKQoKc2lnX3Jlc3VsdHMgPSBzYXBwbHkodGFibGVfd2l0aF9zaWdfcmVzdWx0cywKICAgICAgZnVuY3Rpb24oeClwYXN0ZSh4WyJUUlVFIixjKCIwIiwiMSIpXT4wLGNvbGxhcHNlPSIsIikpCnNpZ19yZXN1bHRzX2NvdW50cyA9IHRhYmxlKHNpZ19yZXN1bHRzKQoKcHJpbnQoIkNvdW50aW5nIHRoZSBudW1iZXIgb2YgbWV0YWJvbGl0ZXMgd2l0aCBwPDAuMDAxIikKcHJpbnQocGFzdGUoIlNpZ25pZmljYW50IGluIHRhcmdldGVkIG9ubHk6IixzaWdfcmVzdWx0c19jb3VudHNbIkZBTFNFLFRSVUUiXSkpCnByaW50KHBhc3RlKCJTaWduaWZpY2FudCBpbiB1bnRhcmdldGVkIG9ubHk6IixzaWdfcmVzdWx0c19jb3VudHNbIlRSVUUsRkFMU0UiXSkpCnByaW50KHBhc3RlKCJTaWduaWZpY2FudCBpbiBib3RoOiIsc2lnX3Jlc3VsdHNfY291bnRzWyJUUlVFLFRSVUUiXSkpCgpgYGAKCkFzIGEgbW9yZSByaWdvcm91cyBhbmFseXNpcywgd2UgdXNlIHRoZSBtZXRhLWFuYWx5c2VzIHRvIG9idGFpbiB1c2VmdWwgc3RhdGlzdGljcyBmb3IgY29tcGFyaXNvbi4KCmBgYHtyLG91dC5oZWlnaHQ9JzUwJScsb3V0LndpZHRoPSc1MCUnLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0KIyBFeHRyYWN0IHNvbWUgdXNlZnVsIHN0YXRpc3RpY3MgcGVyIGFuYWx5c2lzCgojIFAtdmFsdWUgZm9yIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGFyZ2V0ZWQgYW5kIHVudGFyZ2V0ZWQKdGFyZ2V0ZWRfZGlmZl9wID0gCiAgc2FwcGx5KG1ldGFfYW5hbHlzaXNfc3RhdHMsZnVuY3Rpb24oeCl4JHJlX21vZGVsMiRwdmFsWzJdKQoKIyBQLXZhbHVlcyAtIHRhcmdldGVkIHZzLiB1bnRhcmdldGVkCnB2YWxzX3RhciA9IHNhcHBseShtZXRhX2FuYWx5c2lzX3N0YXRzLGZ1bmN0aW9uKHgpeCRyZV9tb2RlbF90YXIkcHZhbCkKcHZhbHNfdW50YXIgPSBzYXBwbHkobWV0YV9hbmFseXNpc19zdGF0cyxmdW5jdGlvbih4KXgkcmVfbW9kZWxfdW50YXIkcHZhbCkKcHZhbHNfdW50YXIgPSB1bmxpc3QocHZhbHNfdW50YXJbc2FwcGx5KHB2YWxzX3VudGFyLGxlbmd0aCk+MF0pCnNpZ25pZmljYW50X2luID0gcmVwKCJOb25lIixsZW5ndGgocHZhbHNfdW50YXIpKQpzaWduaWZpY2FudF9pbltwdmFsc190YXI8MC4wMDFdID0gIlRhcmdldGVkIgpzaWduaWZpY2FudF9pbltwdmFsc191bnRhcjwwLjAwMV0gPSAiVW50YXJnZXRlZCIKc2lnbmlmaWNhbnRfaW5bcHZhbHNfdGFyPDAuMDAxICYgcHZhbHNfdW50YXI8MC4wMDFdID0gIkJvdGgiCnNpZ25pZmljYW50X2RpZmYgPSB0YXJnZXRlZF9kaWZmX3A8MC4wMDEKZGYgPSBkYXRhLmZyYW1lKAogIHRhcmdldGVkID0gLWxvZzEwKHB2YWxzX3RhciksCiAgdW50YXJnZXRlZCA9IC1sb2cxMChwdmFsc191bnRhciksCiAgc2lnbmlmaWNhbnRfaW4gPSBzaWduaWZpY2FudF9pbiwKICBzaWduaWZpY2FudF9kaWZmID0gc2lnbmlmaWNhbnRfZGlmZgopCnJobyA9IGNvcihwdmFsc190YXIscHZhbHNfdW50YXIsbWV0aG9kID0gInNwZWFybWFuIikKcmhvcCA9IGNvci50ZXN0KHB2YWxzX3RhcixwdmFsc191bnRhcixtZXRob2QgPSAic3BlYXJtYW4iKSRwLnZhbHVlCnByaW50KAogIGdncGxvdChkZiwgYWVzKHg9dGFyZ2V0ZWQsIHk9dW50YXJnZXRlZCwKICAgICAgICAgICAgICAgICBzaGFwZT1zaWduaWZpY2FudF9kaWZmLCBjb2xvcj1zaWduaWZpY2FudF9pbikpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBnZ3RpdGxlKHBhc3RlKCItbG9nMTAgcC12YWx1ZXMsIHNwZWFybWFuOiIsZm9ybWF0KHJobyxkaWdpdHM9MiksCiAgICAgICAgICAgICAgICAgICIocD0iLGZvcm1hdChyaG9wLGRpZ2l0cz0zKSwiKSIpKQopCgpwcmludCgiIyMjIFN1bW1hcnkgb2YgZGlmZmVyZW5jZXMgaW4gUkUgbW9kZWxzICMjIyIpCnByaW50KCJNb2RlbCBpcyBzaWduaWZpY2FudCBhdCBwPDAuMDAxOiIpCnByaW50KHRhYmxlKGRmJHNpZ25pZmljYW50X2luKSkKcHJpbnQoIkFkZGluZyBpc190YXJnZXRlZCBhcyBhIGNvdmFyaWF0ZSBoYXMgcDwwLjAwMToiKQpwcmludCh0YWJsZShkZiRzaWduaWZpY2FudF9kaWZmKSkKCiMgQmV0YXMgLSB0YXJnZXRlZCB2cy4gdW50YXJnZXRlZApiZXRhc190YXIgPSBzYXBwbHkobWV0YV9hbmFseXNpc19zdGF0cyxmdW5jdGlvbih4KXgkcmVfbW9kZWxfdGFyJGJldGFbMSwxXSkKYmV0YXNfdW50YXIgPSBzYXBwbHkobWV0YV9hbmFseXNpc19zdGF0cyxmdW5jdGlvbih4KXgkcmVfbW9kZWxfdW50YXIkYmV0YVsxLDFdKQpiZXRhc191bnRhciA9IHVubGlzdChiZXRhc191bnRhcltzYXBwbHkoYmV0YXNfdW50YXIsbGVuZ3RoKT4wXSkKZGYgPSBkYXRhLmZyYW1lKAogIHRhcmdldGVkID0gYmV0YXNfdGFyLAogIHVudGFyZ2V0ZWQgPSBiZXRhc191bnRhciwKICBzaWduaWZpY2FudF9pbiA9IHNpZ25pZmljYW50X2luLAogIHNpZ25pZmljYW50X2RpZmYgPSBzaWduaWZpY2FudF9kaWZmCikKcmhvID0gY29yKGJldGFzX3VudGFyLGJldGFzX3RhcixtZXRob2QgPSAic3BlYXJtYW4iKQpyaG9wID0gY29yLnRlc3QoYmV0YXNfdW50YXIsYmV0YXNfdGFyLG1ldGhvZCA9ICJzcGVhcm1hbiIpJHAudmFsdWUKcHJpbnQoCiAgZ2dwbG90KGRmLCBhZXMoeD10YXJnZXRlZCwgeT11bnRhcmdldGVkLAogICAgICAgICAgICAgICAgIHNoYXBlPXNpZ25pZmljYW50X2RpZmYsIGNvbG9yPXNpZ25pZmljYW50X2luKSkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIGdndGl0bGUocGFzdGUoIkVmZmVjdCBzaXplcywgc3BlYXJtYW46Iixmb3JtYXQocmhvLGRpZ2l0cz0yKSwKICAgICAgICAgICAgICAgICAgIihwPSIsZm9ybWF0KHJob3AsZGlnaXRzPTMpLCIpIikpCikKYGBgCgpGcm9tIHRoZSBwbG90cyBhYm92ZSB3ZSB0YWtlIHRoZSBtb3N0IGV4dHJlbWUgZXhhbXBsZXMgYW5kIGV4YW1pbmUgdGhlaXIgZm9yZXN0IHBsb3RzLgoKYGBge3Isb3V0LmhlaWdodD0nNTAlJyxvdXQud2lkdGg9JzUwJScsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQphZ3JlZV9leGFtcGxlID0gbmFtZXMoc2FtcGxlKHdoaWNoKHB2YWxzX3RhcjwgMWUtMTAgJiBwdmFsc191bnRhciA8IDFlLTEwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRhcmdldGVkX2RpZmZfcCA+IDAuMDEpKVsxXSkKc2ltcGxpZnlfbGFiZWxzX2Zvcl9mb3Jlc3Q8LWZ1bmN0aW9uKHMpewogIHMgPSBnc3ViKCIsdW50YXJnZXRlZCIsIiIscykKICB0aXNzdWUgPSBzdHJzcGxpdChzLHNwbGl0PSIsIilbWzFdXVsxXQogIHMgPSBnc3ViKHBhc3RlKHRpc3N1ZSwiLCIsc2VwPSIiKSwiIixzKQogIHJldHVybihzKQp9CmZvcmVzdChtZXRhX2FuYWx5c2lzX3N0YXRzW1thZ3JlZV9leGFtcGxlXV0kcmVfbW9kZWwxLAogIHNsYWIgPSBzaW1wbGlmeV9sYWJlbHNfZm9yX2ZvcmVzdCgKICAgIG1ldGFfYW5hbHlzaXNfc3RhdHNbW2FncmVlX2V4YW1wbGVdXVtbMV1dWywxXSksCiAgbWFpbiA9IHBhc3RlKGFncmVlX2V4YW1wbGUsInNpZ25pZmljYW50IGluIGJvdGgsIHRhciBhbmQgdW50YXIgYWdyZWUiLHNlcD0iXG4iKSwKICB4bGFiID0gIkxvZyBmYyIsY29sID0gImJsdWUiKQoKYWdyZWVfcF9kaXNhZ3JlZV9iZXRhID0gbmFtZXMoc2FtcGxlKHdoaWNoKHB2YWxzX3RhcjwgMWUtMTAgJiBwdmFsc191bnRhciA8IDFlLTEwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRhcmdldGVkX2RpZmZfcCA8IDAuMDAxKSlbMV0pCmZvcmVzdChtZXRhX2FuYWx5c2lzX3N0YXRzW1thZ3JlZV9wX2Rpc2FncmVlX2JldGFdXSRyZV9tb2RlbDEsCiAgc2xhYiA9IHNpbXBsaWZ5X2xhYmVsc19mb3JfZm9yZXN0KAogICAgbWV0YV9hbmFseXNpc19zdGF0c1tbYWdyZWVfcF9kaXNhZ3JlZV9iZXRhXV1bWzFdXVssMV0pLAogIG1haW4gPSBwYXN0ZShhZ3JlZV9wX2Rpc2FncmVlX2JldGEsCiAgICAgICAgICAgICAgICJzaWduaWZpY2FudCBpbiBib3RoLCB0YXIgYW5kIHVudGFyIGRpc2FncmVlIixzZXA9IlxuIiksCiAgeGxhYiA9ICJMb2cgZmMiLGNvbCA9ICJibHVlIikKCmRpc2FncmVlX2V4YW1wbGUxID0gbmFtZXMoc2FtcGxlKHdoaWNoKHB2YWxzX3RhcjwgMWUtMjAgJiBwdmFsc191bnRhciA+MC4xKSlbMV0pCmZvcmVzdChtZXRhX2FuYWx5c2lzX3N0YXRzW1tkaXNhZ3JlZV9leGFtcGxlMV1dJHJlX21vZGVsMSwKICBzbGFiID0gc2ltcGxpZnlfbGFiZWxzX2Zvcl9mb3Jlc3QoCiAgICBtZXRhX2FuYWx5c2lzX3N0YXRzW1tkaXNhZ3JlZV9leGFtcGxlMV1dW1sxXV1bLDFdKSwKICBtYWluID0gcGFzdGUoZGlzYWdyZWVfZXhhbXBsZTEsCiAgICAgICAgICAgICAgICJzaWduaWZpY2FudCB0YXJnZXRlZCwgdGFyIGFuZCB1bnRhciBkaXNhZ3JlZSIsc2VwPSJcbiIpLAogIHhsYWIgPSAiTG9nIGZjIixjb2wgPSAiYmx1ZSIpCgoKZGlzYWdyZWVfZXhhbXBsZTIgPSBuYW1lcyhzYW1wbGUod2hpY2gocHZhbHNfdGFyID4gMC4xICYgcHZhbHNfdW50YXIgPCAxZS0yMCkpWzFdKQpmb3Jlc3QobWV0YV9hbmFseXNpc19zdGF0c1tbZGlzYWdyZWVfZXhhbXBsZTJdXSRyZV9tb2RlbDEsCiAgc2xhYiA9IHNpbXBsaWZ5X2xhYmVsc19mb3JfZm9yZXN0KAogICAgbWV0YV9hbmFseXNpc19zdGF0c1tbZGlzYWdyZWVfZXhhbXBsZTJdXVtbMV1dWywxXSksCiAgbWFpbiA9IHBhc3RlKGRpc2FncmVlX2V4YW1wbGUyLAogICAgICAgICAgICAgICAic2lnbmlmaWNhbnQgaW4gdW50YXJnZXRlZCwgdGFyIGFuZCB1bnRhciBkaXNhZ3JlZSIsc2VwPSJcbiIpLAogIHhsYWIgPSAiTG9nIGZjIixjb2wgPSAiYmx1ZSIpCgpgYGAKCgojIFRhcmdldGVkIHZzLiB1bnRhcmdldGVkOiBjb21wYXJpc29uIGFzIGEgcHJlZGljdGlvbiB0YXNrCgpVc2UgNS1mb2xkIGNyb3NzIHZhbGlkYXRpb24gZm9yIGFuYWx5c2lzIHdpdGhpbiB0aXNzdWVzLiBGb3IgZWFjaCBwYWlyIG9mIHRhcmdldGVkIGFuZCB1bnRhcmdldGVkIGRhdGFzZXRzIGZyb20gdGhlIHNhbWUgdGlzc3VlLCB3ZSB1c2UgdGhlIHVudGFyZ2V0ZWQgZGF0YSBhcyB0aGUgcHJlZGljdGl2ZSBmZWF0dXJlcyBhbmQgYWxsIG1ldGFib2xpdGVzIGluIHRoZSB0YXJnZXRlZCBkYXRhc2V0cyBhcyB0aGUgZGVwZW5kZW50IHZhcmlhYmxlcy4gVGhlIGNvZGUgYmVsb3cgdXNlcyBmZWF0dXJlIHNlbGVjdGlvbiBhbmQgcmFuZG9tIGZvcmVzdHMgdG8gdHJhaW4gdGhlIHByZWRpY3RpdmUgbW9kZWxzLiAKCmBgYHtyLG91dC5oZWlnaHQ9JzUwJScsb3V0LndpZHRoPSc1MCUnLGV2YWw9RkFMU0UsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQpuZm9sZHMgPSA1CnByZWRpY3Rpb25fYW5hbHlzaXNfcmVzdWx0cyA9IGxpc3QoKQpmb3Iobm4xIGluIG5hbWVzKG1ldGFib2xvbWljc19wcm9jZXNzZWRfZGF0YXNldHMpKXsKICBubjFfdGlzc3VlID0gc3Ryc3BsaXQobm4xLHNwbGl0PSIsIilbWzFdXVsxXQogIG5uMV90aXNzdWUgPSBnc3ViKCJfcG93ZGVyIiwiIixubjFfdGlzc3VlKQogIGlmKGdyZXBsKCJ1bnRhcmdldGVkIixubjEpKXtuZXh0fQogIGZvcihubjIgaW4gbmFtZXMobWV0YWJvbG9taWNzX3Byb2Nlc3NlZF9kYXRhc2V0cykpewogICAgaWYobm4yID09IG5uMSl7bmV4dH0KICAgIGlmKCFncmVwbCgidW50YXJnZXRlZCIsbm4yKSl7bmV4dH0KICAgIG5uMl90aXNzdWUgPSBzdHJzcGxpdChubjIsc3BsaXQ9IiwiKVtbMV1dWzFdCiAgICBubjJfdGlzc3VlID0gZ3N1YigiX3Bvd2RlciIsIiIsbm4yX3Rpc3N1ZSkKICAgIG5uMl9kYXRhc2V0ID0gc3Ryc3BsaXQobm4yLHNwbGl0PSIsIilbWzFdXVsyXQogICAgaWYobm4xX3Rpc3N1ZSE9bm4yX3Rpc3N1ZSl7bmV4dH0KICAgIHByaW50KHBhc3RlKCJmZWF0dXJlcyBmcm9tOiIsbm4yKSkKICAgIHByaW50KHBhc3RlKCJsYWJlbHMgZnJvbToiLG5uMSkpCiAgICAjIGdldCB0aGUgbnVtZXJpYyBkYXRhc2V0cyBhbmQgdGhlaXIgYW5ub3RhdGlvbgogICAgeSA9IG1ldGFib2xvbWljc19wcm9jZXNzZWRfZGF0YXNldHNbW25uMV1dJHNhbXBsZV9kYXRhCiAgICB4ID0gbWV0YWJvbG9taWNzX3Byb2Nlc3NlZF9kYXRhc2V0c1tbbm4yXV0kc2FtcGxlX2RhdGEKICAgICMgYWxpZ24gdGhlIHNhbXBsZSBzZXRzCiAgICBiaWRfeSA9IG1lcmdlZF9kbWFxY19kYXRhW2NvbG5hbWVzKHkpLCJiaWQiXQogICAgYmlkX3ggPSBtZXJnZWRfZG1hcWNfZGF0YVtjb2xuYW1lcyh4KSwiYmlkIl0gICAgCiAgICAjIHN0ZXAgMTogbWVyZ2Ugc2FtcGxlcyBmcm9tIHRoZSBzYW1lIEJJRAogICAgaWYobGVuZ3RoKHVuaXF1ZShiaWRfeCkpIT1sZW5ndGgoYmlkX3gpKXsKICAgICAgeCA9IGFnZ3JlZ2F0ZV9yZXBlYXRlZF9zYW1wbGVzKHgsYmlkX3gpCiAgICB9CiAgICBlbHNlewogICAgICBjb2xuYW1lcyh4KSA9IGJpZF94CiAgICB9CiAgICBpZihsZW5ndGgodW5pcXVlKGJpZF95KSkhPWxlbmd0aChiaWRfeSkpewogICAgICB5ID0gYWdncmVnYXRlX3JlcGVhdGVkX3NhbXBsZXMoeSxiaWRfeSkKICAgIH1lbHNlewogICAgICBjb2xuYW1lcyh5KSA9IGJpZF95CiAgICB9CiAgICAjIHN0ZXAgMjogdXNlIHRoZSBzaGFyZWQgYmlvIGlkcwogICAgc2hhcmVkX2JpZHMgPSBhcy5jaGFyYWN0ZXIoaW50ZXJzZWN0KGNvbG5hbWVzKHkpLGNvbG5hbWVzKHgpKSkKICAgIHggPSB0KGFzLm1hdHJpeCh4WyxzaGFyZWRfYmlkc10pKQogICAgeSA9IHQoYXMubWF0cml4KHlbLHNoYXJlZF9iaWRzXSkpCiAgICAjIEF0IHRoaXMgcG9pbnQgeCBhbmQgeSBhcmUgb3ZlciB0aGUgc2FtZSBCSURzLCBub3cgd2UgYWRkIHRoZSBtZXRhZGF0YQogICAgeV9tZXRhID0gdW5pcXVlKG1ldGFib2xvbWljc19wcm9jZXNzZWRfZGF0YXNldHNbW25uMV1dJHNhbXBsZV9tZXRhX3BhcnNlZCkKICAgIHJvd25hbWVzKHlfbWV0YSkgPSB5X21ldGEkYmlkCiAgICB5X21ldGEgPSB5X21ldGFbc2hhcmVkX2JpZHMsXQogICAgCiAgICAjIHRha2UgdGhlIGNvdmFyaWF0ZXMgKGlnbm9yZSBkaXN0YW5jZXMpCiAgICBjdXJyX2Nvdl9jb2xzID0gaW50ZXJzZWN0KGNvbG5hbWVzKHlfbWV0YSksYmlvc3BlY19jb2xzWzJdKQogICAgY3Vycl9jb3ZzID0gZGF0YS5mcmFtZSh5X21ldGFbLGN1cnJfY292X2NvbHNdKQogICAgbmFtZXMoY3Vycl9jb3ZzKSA9IGN1cnJfY292X2NvbHMKICAgIGN1cnJfY292cyRzZXggPSB5X21ldGEkYW5pbWFsLnJlZ2lzdHJhdGlvbi5zZXggIyBhZGQgc2V4CiAgICAjIGFkZCB0aGUgY292YXJpYXRlcyBpbnRvIHgKICAgIHggPSBjYmluZCh4LGN1cnJfY292cykKICAgIAogICAgIyBSdW4gdGhlIHJlZ3Jlc3Npb25zCiAgICBmb2xkcyA9IHNhbXBsZShyZXAoMTpuZm9sZHMsKDErbnJvdyh4KS9uZm9sZHMpKSlbMTpucm93KHgpXQogICAgbnVtRmVhdHVyZXMgPSBtaW4obmNvbCh4KSwyMDAwKQogICAgcHJlZHMgPSBjKCk7cmVhbD1jKCkKICAgIGZvcihpIGluIDE6bmNvbCh5KSl7CiAgICAgIGlmKCBpICUlIDEwID09IDApe3ByaW50KHBhc3RlKCJhbmFseXppbmcgbWV0YWJvbGl0ZSBudW1iZXI6IixpKSl9CiAgICAgIHlfaSA9IHlbLDFdCiAgICAgIGlfcHJlZHMgPSBjKCk7aV9yZWFsPWMoKQogICAgICBmb3IoaiBpbiAxOm5mb2xkcyl7CiAgICAgICAgdHJfeCA9IHhbZm9sZHMhPWosXQogICAgICAgIHRyX3lpID0geV9pW2ZvbGRzIT1qXQogICAgICAgIHRlX3ggPSB4W2ZvbGRzPT1qLF0KICAgICAgICB0ZV95ID0geV9pW2ZvbGRzPT1qXQogICAgICAgICMgcmFuZG9tIGZvcmVzdAogICAgICAgICMgbW9kZWwgPSByYW5kb21Gb3Jlc3QodHJfeWkseD10cl94LG50cmVlID0gMjApCiAgICAgICAgIyB0ZV9wcmVkcyA9IHByZWRpY3QobW9kZWwsbmV3ZGF0YSA9IHRlX3gpCiAgICAgICAgbW9kZWwgPSBmZWF0dXJlX3NlbGVjdGlvbl93cmFwcGVyKHRyX3gsdHJfeWksCiAgICAgICAgICAgICAgICAgICBjb2VmZl9vZl92YXIscmFuZG9tRm9yZXN0LAogICAgICAgICAgICAgICAgICAgdG9wSyA9IG51bUZlYXR1cmVzLG50cmVlPTUwKQogICAgICAgIHRlX3ByZWRzID0gcHJlZGljdChtb2RlbCxuZXdkYXRhID0gdGVfeCkKICAgICAgICBpX3ByZWRzID0gYyhpX3ByZWRzLHRlX3ByZWRzKQogICAgICAgIGlfcmVhbCA9IGMoaV9yZWFsLHRlX3kpCiAgICAgIH0KICAgICAgcHJlZHMgPSBjYmluZChwcmVkcyxpX3ByZWRzKQogICAgICByZWFsID0gY2JpbmQocmVhbCxpX3JlYWwpCiAgICB9CiAgICBjb2xuYW1lcyhwcmVkcykgPSBjb2xuYW1lcyh5KQogICAgY29sbmFtZXMocmVhbHMpID0gY29sbmFtZXMoeSkKICAgIGN1cnJuYW1lID0gcGFzdGUobm4xLG5uMixzZXA9IjsiKQogICAgcHJlZGljdGlvbl9hbmFseXNpc19yZXN1bHRzW1tjdXJybmFtZV1dID0gbGlzdCgKICAgICAgcHJlZHMgPSBwcmVkcyxyZWFsPXJlYWwKICAgICkKICB9Cn0Kc2F2ZV90b19idWNrZXQocHJlZGljdGlvbl9hbmFseXNpc19yZXN1bHRzLAogICAgICAgICAgICAgICBmaWxlPSJ0YXJfdnNfdW50YXJfcHJlZGljdGlvbl9hbmFseXNpc19yZXN1bHRzLlJEYXRhIiwKICAgICAgICAgICAgICAgYnVja2V0ID0gImdzOi8vYmljX2RhdGFfYW5hbHlzaXMvcGFzczFhL21ldGFib2xvbWljcy8iKQpgYGAKCldlIG5vdyB0YWtlIHRoZSBwcmVkaWN0ZWQgYW5kIHJlYWwgdmFsdWVzIGFuZCBlc3RpbWF0ZSB0aGUgcHJlZGljdGlvbiBhY2N1cmFjeSBpbiBkaWZmZXJlbnQgd2F5cy4gCgpgYGB7cixvdXQuaGVpZ2h0PSc1MCUnLG91dC53aWR0aD0nNTAlJyxldmFsPVRSVUUsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQoKcHJlZGljdGlvbl9hbmFseXNpc19yZXN1bHRzID0gIGxvYWRfZnJvbV9idWNrZXQoCiAgInRhcl92c191bnRhcl9wcmVkaWN0aW9uX2FuYWx5c2lzX3Jlc3VsdHMuUkRhdGEiLAogICAgImdzOi8vYmljX2RhdGFfYW5hbHlzaXMvcGFzczFhL21ldGFib2xvbWljcy8iLEYpCnByZWRpY3Rpb25fYW5hbHlzaXNfcmVzdWx0cyA9IHByZWRpY3Rpb25fYW5hbHlzaXNfcmVzdWx0c1tbMV1dCgpyZXN1bHRzX21ldHJpY3MgPSBsaXN0KCkKZm9yKG5uIGluIG5hbWVzKHByZWRpY3Rpb25fYW5hbHlzaXNfcmVzdWx0cykpewogIHByZWRzID0gcHJlZGljdGlvbl9hbmFseXNpc19yZXN1bHRzW1tubl1dJHByZWRzCiAgcmVhbCA9IHByZWRpY3Rpb25fYW5hbHlzaXNfcmVzdWx0c1tbbm5dXSRyZWFsCiAgdGFyX25hbWUgPSBzdHJzcGxpdChubixzcGxpdD0iOyIpW1sxXV1bMV0KICB1bnRhcl9uYW1lID0gc3Ryc3BsaXQobm4sc3BsaXQ9IjsiKVtbMV1dWzJdCiAgeSA9IG1ldGFib2xvbWljc19wcm9jZXNzZWRfZGF0YXNldHNbW3Rhcl9uYW1lXV0kc2FtcGxlX2RhdGEKICBjb2xuYW1lcyhwcmVkcykgPSByb3duYW1lcyh5KQogIGNvbG5hbWVzKHJlYWwpID0gcm93bmFtZXMoeSkKICB0YXJfbmFtZSA9IHNpbXBsaWZ5X21ldGFiX2RhdGFzZXRfbmFtZSh0YXJfbmFtZSkKICB1bnRhcl9uYW1lID0gc2ltcGxpZnlfbWV0YWJfZGF0YXNldF9uYW1lKHVudGFyX25hbWUpCiAgY3VycnRpc3N1ZSA9IHN0cnNwbGl0KHRhcl9uYW1lLHNwbGl0PSIsIilbWzFdXVsxXQogIHRhcl9uYW1lID0gZ3N1YihwYXN0ZShjdXJydGlzc3VlLCIsIixzZXA9IiIpLCIiLHRhcl9uYW1lKQogIHVudGFyX25hbWUgPSBnc3ViKHBhc3RlKGN1cnJ0aXNzdWUsIiwiLHNlcD0iIiksIiIsdW50YXJfbmFtZSkKICBpZighIGN1cnJ0aXNzdWUgJWluJSBuYW1lcyhyZXN1bHRzX21ldHJpY3MpKXsKICAgIHJlc3VsdHNfbWV0cmljc1tbY3VycnRpc3N1ZV1dID0gbGlzdCgpCiAgfQogIGlmKCEgdGFyX25hbWUgJWluJSBuYW1lcyhyZXN1bHRzX21ldHJpY3NbW2N1cnJ0aXNzdWVdXSkpewogICAgcmVzdWx0c19tZXRyaWNzW1tjdXJydGlzc3VlXV1bW3Rhcl9uYW1lXV0gPSBsaXN0KCkKICB9CiAgCiAgcmhvcyA9IGZvcm1hdChkaWFnKGNvcihwcmVkcyxyZWFsLG1ldGhvZD0ic3BlYXJtYW4iKSksZGlnaXRzPTMpCiAgcmhvcyA9IGFzLm51bWVyaWMocmhvcykKICBTRXMgPSBjb2xTdW1zKChwcmVkcy1yZWFsKV4yKQogIE1TRXMgPSBTRXMgLyBucm93KHByZWRzKQogIFJNU0UgPSBzcXJ0KE1TRXMpCiAgck1TRSA9IE1TRXMgLyBhcHBseSh5LDEsdmFyKQogIENvVnMgPSBhcHBseSh5LDEsc2QpIC8gYXBwbHkoeSwxLG1lYW4pCiAgZGlzY0NvVnMgPSBjdXQoQ29WcyxicmVha3MgPSAyLG9yZGVyZWRfcmVzdWx0ID0gVCkKICAKICByZXN1bHRzX21ldHJpY3NbW2N1cnJ0aXNzdWVdXVtbdGFyX25hbWVdXVtbdW50YXJfbmFtZV1dID0gZGF0YS5mcmFtZSgKICAgIHJob3MsTVNFcyxSTVNFLHJNU0UsQ29WcyxkaXNjQ29WcwogICkKfQoKYGBgCgpXZSBub3cgcHJlc2VudCBhIGZldyBzdW1tYXJ5IHBsb3RzLgoKYGBge3Isb3V0LmhlaWdodD0nNTAlJyxvdXQud2lkdGg9JzUwJScsZXZhbD1UUlVFLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0KZm9yKHRpc3N1ZSBpbiBuYW1lcyhyZXN1bHRzX21ldHJpY3MpKXsKICBmb3IodGFyIGluIG5hbWVzKHJlc3VsdHNfbWV0cmljc1tbdGlzc3VlXV0pKXsKICAgIGwgPSByZXN1bHRzX21ldHJpY3NbW3Rpc3N1ZV1dW1t0YXJdXQogICAgcmhvX3ZzX2N2ID0gYygpCiAgICBmb3IodW50YXIgaW4gbmFtZXMobCkpewogICAgICBtID0gbFtbdW50YXJdXVssYygicmhvcyIsImRpc2NDb1ZzIildICMgdGFrZSB0aGUgY3VycmVudCBtYXRyaXgKICAgICAgbSA9IGNiaW5kKHJlcCh1bnRhcixucm93KG0pKSxtKQogICAgICBtJGRpc2NDb1ZzID0gYXMubnVtZXJpYyhtJGRpc2NDb1ZzKQogICAgICByaG9fdnNfY3YgPSByYmluZChyaG9fdnNfY3YsbSkKICAgIH0KICAgIGNvbG5hbWVzKHJob192c19jdilbMV0gPSAiZGF0YXNldCIKICAgIGJveHBsb3Qocmhvc35kaXNjQ29WczpkYXRhc2V0LGRhdGE9cmhvX3ZzX2N2LGxhcz0yLAogICAgICAgICAgICB5bGFiPSJTcGVhcm1hbiIseGxhYiA9ICIiLHlsaW09YygwLDEpLAogICAgICAgICAgICBtYWluID0gcGFzdGUodGlzc3VlLHRhcixzZXA9IiwiKSkKICB9Cn0KCgpgYGAKCkFzIGFkZGl0aW9uYWwgcmVmZXJlbmNlcywgd2UgdHJhaW4gYmVsb3cgYWRkaXRpb25hbCBtb2RlbHMuIEZpcnN0LCB3ZSBjaGVjayB0aGUgcHJlZGljdGlvbiBvZiBuYWl2ZSBtb2RlbHMgdGhhdCB1c2UgdGVjaG5pY2FsIGFuZCBjbGluaWNhbCBjb3ZhcmlhdGVzIG9ubHkuIFNlY29uZCwgd2UgdXNlIG11bHRpLXRhc2sgcmVncmVzc2lvbiBhbmQgZGVlcCBsZWFybmluZyBtb2RlbHMuCgpgYGB7cixvdXQuaGVpZ2h0PSc1MCUnLG91dC53aWR0aD0nNTAlJyxldmFsPUZBTFNFLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0KCmNvdl9wcmVkaWN0aW9uX2FuYWx5c2lzX3Jlc3VsdHMgPSBsaXN0KCkKZm9yKG5uMSBpbiBuYW1lcyhtZXRhYm9sb21pY3NfcHJvY2Vzc2VkX2RhdGFzZXRzKSl7CiAgbm4xX3Rpc3N1ZSA9IHN0cnNwbGl0KG5uMSxzcGxpdD0iLCIpW1sxXV1bMV0KICBubjFfdGlzc3VlID0gZ3N1YigiX3Bvd2RlciIsIiIsbm4xX3Rpc3N1ZSkKICBpZihncmVwbCgidW50YXJnZXRlZCIsbm4xKSl7bmV4dH0KICBwcmludChubjEpCiAgeSA9IG1ldGFib2xvbWljc19wcm9jZXNzZWRfZGF0YXNldHNbW25uMV1dJHNhbXBsZV9kYXRhCiAgeV92aWFscyA9IGNvbG5hbWVzKHkpCiAgYmlkX3kgPSBtZXJnZWRfZG1hcWNfZGF0YVtjb2xuYW1lcyh5KSwiYmlkIl0KICBjb2xuYW1lcyh5KSA9IGJpZF95CiAgeSA9IHQoYXMubWF0cml4KHkpKQogIGlmKG5jb2woeSk+MTAwMCl7bmV4dH0KICBjb3ZfY29scyA9IGMoImFuaW1hbC5yZWdpc3RyYXRpb24uc2V4IiwKICAgICAgICAgICAgICJhY3V0ZS50ZXN0LndlaWdodCIsCiAgICAgICAgICAgICAiYWN1dGUudGVzdC5kaXN0YW5jZSIsCiAgICAgICAgICAgICAiYW5pbWFsLmtleS50aW1lcG9pbnQiKQogIGNvdnMgPSBtZXJnZWRfZG1hcWNfZGF0YVt5X3ZpYWxzLGNvdl9jb2xzXQogIHggPSBjb3ZzCiAgCiAgIyBSdW4gdGhlIHJlZ3Jlc3Npb25zCiAgZm9sZHMgPSBzYW1wbGUocmVwKDE6bmZvbGRzLCgxK25yb3coeCkvbmZvbGRzKSkpWzE6bnJvdyh4KV0KICBudW1GZWF0dXJlcyA9IG1pbihuY29sKHgpLDIwMDApCiAgcHJlZHMgPSBjKCk7cmVhbD1jKCkKICBmb3IoaSBpbiAxOm5jb2woeSkpewogICAgeV9pID0geVssMV0KICAgIGlfcHJlZHMgPSBjKCk7aV9yZWFsPWMoKQogICAgZm9yKGogaW4gMTpuZm9sZHMpewogICAgICBwcmludChqKQogICAgICB0cl94ID0geFtmb2xkcyE9aixdCiAgICAgIHRyX3lpID0geV9pW2ZvbGRzIT1qXQogICAgICB0ZV94ID0geFtmb2xkcz09aixdCiAgICAgIHRlX3kgPSB5X2lbZm9sZHM9PWpdCiAgICAgICMgcmFuZG9tIGZvcmVzdAogICAgICBtb2RlbCA9IHJhbmRvbUZvcmVzdCh0cl95aSx4PXRyX3gsbnRyZWUgPSAyMCkKICAgICAgdGVfcHJlZHMgPSBwcmVkaWN0KG1vZGVsLG5ld2RhdGEgPSB0ZV94KQogICAgICBpX3ByZWRzID0gYyhpX3ByZWRzLHRlX3ByZWRzKQogICAgICBpX3JlYWwgPSBjKGlfcmVhbCx0ZV95KQogICAgfQogICAgcHJlZHMgPSBjYmluZChwcmVkcyxpX3ByZWRzKQogICAgcmVhbCA9IGNiaW5kKHJlYWwsaV9yZWFsKQogIH0KICBjb3ZfcHJlZGljdGlvbl9hbmFseXNpc19yZXN1bHRzW1tubjFdXSA9IGxpc3QoCiAgICAgIHByZWRzID0gcHJlZHMscmVhbD1yZWFsCiAgICApCn0KCiMgcHJlZHMgPSBjKCk7cmVhbD1jKCkKIyBmb3IoaiBpbiAxOm5mb2xkcyl7CiMgICB0cl94ID0geFtmb2xkcyE9aixdCiMgICB0cl95ID0geVtmb2xkcyE9aixdCiMgICB0ZV94ID0geFtmb2xkcz09aixdCiMgICB0ZV95ID0geVtmb2xkcz09aixdCiMgICBtb2RlbCA9IE1UTF93cmFwcGVyKHRyX3gsdHJfeSx0eXBlPSJSZWdyZXNzaW9uIiwgUmVndWxhcml6YXRpb249IkwyMSIpCiMgICB0ZV9wcmVkcyA9IHByZWRpY3QobW9kZWwsdGVfeCkKIyAgIHJlYWwgPSByYmluZChyZWFsLHRlX3kpCiMgICBwcmVkcyA9IHJiaW5kKHByZWRzLHRlX3ByZWRzKQojIH0KIyBkaWFnKGNvcihwcmVkcyxyZWFsKSkKCiMgVXNpbmcgUExTIHJlZ3Jlc3Npb24KIyBsaWJyYXJ5KHBscykKIyBwbHNfbW9kZWwgPSBwbHNyKHl+eCxuY29tcCA9IDUsdmFsaWRhdGlvbj0iTE9PIikKIyBldmFsID0gTVNFUChwbHNfbW9kZWwpCiMgCiMgeV9wY2EgPSBwcmNvbXAoeSkKIyBwbG90KHlfcGNhKQojIGV4cGxhaW5lZF92YXIgPSB5X3BjYSRzZGV2XjIvc3VtKHlfcGNhJHNkZXZeMikKIyB5X3BjYV9tYXRyaXggPSB5X3BjYSR4WywxOjEwXQojIAojICMgcmVncmVzcyBvdXQgc2V4LCB3ZWlnaHQKIyAKIyBnZXRfZXhwbGFpbmVkX3ZhcmlhbmNlX3VzaW5nX1BDQSh4LHkpCiMgeCA9IGFwcGx5KHgsMixyZWdyZXNzX291dCxjb3ZzPWNvdnMpCiMgeSA9IGFwcGx5KHksMixyZWdyZXNzX291dCxjb3ZzPWNvdnMpCiMgZ2V0X2V4cGxhaW5lZF92YXJpYW5jZV91c2luZ19QQ0EoeCx5KQoKCmBgYAoKPCEtLSAjIyBDb21wYXJpc29uIG9mIGNvdmFyaWFuY2UgbWF0cmljZXMgLS0+Cgo8IS0tIGBgYHtyfSAtLT4KCjwhLS0gQ1YgPC1mdW5jdGlvbih4KXsgLS0+CjwhLS0gICByZXR1cm4oc2QoeCkvbWVhbih4KSkgLS0+CjwhLS0gfSAtLT4KCjwhLS0gIyBHZXQgYWxsIGNvcnJlbGF0aW9uIGFuZCBjb3ZhcmlhbmNlIG1hdHJpY2VzIC0tPgo8IS0tIHkgPSBtZXRhYm9sb21pY3NfcGFyc2VkX2RhdGFzZXRzW1sxXV0kc2FtcGxlX2RhdGEgIyBhbmNob3IgYWxsIGRhdGFzZXRzIGJ5IHRoZXNlIGlkcyAtLT4KPCEtLSB5X3ZpYWxzID0gY29sbmFtZXMoeSkgLS0+CjwhLS0gYmlkX3kgPSBhcy5jaGFyYWN0ZXIobWVyZ2VkX2RtYXFjX2RhdGFbY29sbmFtZXMoeSksImJpZCJdKSAtLT4KPCEtLSBjb3ZfY29scyA9IGMoImFuaW1hbC5yZWdpc3RyYXRpb24uc2V4IiwgLS0+CjwhLS0gICAgICAgICAgICAgICJhY3V0ZS50ZXN0LndlaWdodCIsIC0tPgo8IS0tICAgICAgICAgICAgICAiYWN1dGUudGVzdC5kaXN0YW5jZSIpIC0tPgo8IS0tIGNvdnMgPSBtZXJnZWRfZG1hcWNfZGF0YVt5X3ZpYWxzLGNvdl9jb2xzXSAtLT4KCjwhLS0gY292X21hdHJpY2VzID0gbGlzdCgpIC0tPgo8IS0tIGNvcl9tYXRyaWNlcyA9IGxpc3QoKSAtLT4KPCEtLSBmb3Iobm4gaW4gbmFtZXMobWV0YWJvbG9taWNzX3BhcnNlZF9kYXRhc2V0cykpeyAtLT4KPCEtLSAgIHggPSBtZXRhYm9sb21pY3NfcGFyc2VkX2RhdGFzZXRzW1tubl1dJHNhbXBsZV9kYXRhIC0tPgo8IS0tICAgYmlkX3ggPSBhcy5jaGFyYWN0ZXIobWVyZ2VkX2RtYXFjX2RhdGFbY29sbmFtZXMoeCksImJpZCJdKSAtLT4KPCEtLSAgIHByaW50KHN1bSghaXMuZWxlbWVudChiaWRfeSxzZXQ9YmlkX3gpKSkgIyBzaG91bGQgYmUgemVybyAtLT4KPCEtLSAgIGNvbG5hbWVzKHgpID0gYmlkX3ggLS0+CjwhLS0gICB4ID0geFssYmlkX3ldIC0tPgo8IS0tICAgIyB4ID0gYXBwbHkoeCwxLHJlZ3Jlc3Nfb3V0LGNvdnM9Y292cykgLS0+CjwhLS0gICAjIHggPSB0KHgpIC0tPgo8IS0tICAgIyBjdnMgPSBhcHBseSh4LDEsQ1YpIC0tPgo8IS0tICAgIyBwcmludCh0YWJsZShjdnM+MC41KSkgLS0+CjwhLS0gICAjIHggPSB4W2N2cz4wLjUsXSAtLT4KPCEtLSAgICMgcGNheCA9IHQocHJjb21wKHQoeCksc2NhbGUuID0gRikkeFssMToxMF0pIC0tPgo8IS0tICAgY292X21hdHJpY2VzW1tubl1dID0gY292KHgpIC0tPgo8IS0tICAgY29yX21hdHJpY2VzW1tubl1dID0gY29yKHgpIC0tPgo8IS0tIH0gLS0+CjwhLS0gc2FwcGx5KGNvcl9tYXRyaWNlcyxkaW0pIC0tPgo8IS0tIGxpYnJhcnkoJ2V2b2xxZycpO2xpYnJhcnkoY29ycnBsb3QpIC0tPgo8IS0tIG1hbnRlbF90ZXN0cz0gTWFudGVsQ29yKGNvcl9tYXRyaWNlcykgLS0+CjwhLS0gbWNvcnM9IE1hdHJpeENvcihjb3JfbWF0cmljZXMpIC0tPgo8IS0tIG1jb3JzID0gYXMubWF0cml4KGZvcmNlU3ltbWV0cmljKG1jb3JzLHVwbG89IkwiKSkgLS0+CjwhLS0gbWFudGVsX3Rlc3RzID0gYXMubWF0cml4KCAtLT4KPCEtLSAgIGZvcmNlU3ltbWV0cmljKG1hbnRlbF90ZXN0cyRwcm9iYWJpbGl0aWVzLHVwbG8gPSAiTCIpKSAtLT4KPCEtLSAjIGRpYWcobWFudGVsX3Rlc3RzKT0wIC0tPgo8IS0tIG9yZCA9IGNvcnJwbG90KHQobWNvcnMpLG9yZGVyPSJoY2x1c3QiKSAtLT4KPCEtLSBvcmQgPSByb3duYW1lcyhvcmQpIC0tPgo8IS0tIGNvcnJwbG90KG1jb3JzW29yZCxvcmRdLHAubWF0PW1hbnRlbF90ZXN0c1tvcmQsb3JkXSwgLS0+CjwhLS0gICAgICAgICAgaW5zaWcgPSAibGFiZWxfc2lnIixtZXRob2Q9InNoYWRlIiwgLS0+CjwhLS0gICAgICAgICAgc2lnLmxldmVsID0gYyguMDAwMSwgLjAwMSwgLjAxKSwgLS0+CjwhLS0gICAgICAgICAgcGNoLmNleCA9IC45LCBwY2guY29sID0gImJsYWNrIikgLS0+CjwhLS0gIyByc19yZXMgPSBSYW5kb21Ta2V3ZXJzKGNvcl9tYXRyaWNlcykgLS0+CjwhLS0gIyBjb3JycGxvdChyc19yZXMkY29ycmVsYXRpb25zLCAtLT4KPCEtLSAjICAgICAgICAgIHAubWF0PW1hbnRlbF90ZXN0cyRwcm9iYWJpbGl0aWVzLHR5cGU9Imxvd2VyIiwgLS0+CjwhLS0gIyAgICAgICAgICBpbnNpZyA9ICJsYWJlbF9zaWciLG1ldGhvZD0ic2hhZGUiLCAtLT4KPCEtLSAjICAgICAgICAgIHNpZy5sZXZlbCA9IGMoLjAwMDEsIC4wMDEsIC4wMSksIC0tPgo8IS0tICMgICAgICAgICAgcGNoLmNleCA9IC45LCBwY2guY29sID0gImJsYWNrIix0bC5jZXg9MC42KSAtLT4KCjwhLS0gbGlicmFyeShwc3ljaCkgLS0+Cgo8IS0tIHIxID0gY29yX21hdHJpY2VzW1sxXV0gLS0+CjwhLS0gcjIgPSBjb3JfbWF0cmljZXNbWzhdXSAtLT4KPCEtLSBjb3IocjFbbG93ZXIudHJpKHIxKV0scjJbbG93ZXIudHJpKHIyKV0pIC0tPgo8IS0tIG1hbnRlbF90ZXN0c1sxLDhdIC0tPgo8IS0tIG1hbnRlbF90ZXN0c1s4LDFdIC0tPgoKPCEtLSB0aHJlc2hvbGRfY29tcCA9IGFwcGx5X2Z1bmN0aW9uX29uX3BhaXJzKGNvcl9tYXRyaWNlcywgLS0+CjwhLS0gICAgICAgICAgIGZ1bmN0aW9uKHgseSlzdW0oeD4wLjcmeT4wLjcpKSAtLT4KPCEtLSBjb3JycGxvdCh0aHJlc2hvbGRfY29tcCxpcy5jb3JyID0gRixvcmRlcj0iaGNsdXN0IikgLS0+Cgo8IS0tIHRocmVzaG9sZF9jb21wID0gYXBwbHlfZnVuY3Rpb25fb25fcGFpcnMoY29yX21hdHJpY2VzLCAtLT4KPCEtLSAgICAgICAgICBmdW5jdGlvbih4LHkpbWVhbihkaWFnKGNvcih4LHkpKSkpIC0tPgo8IS0tIGNvcnJwbG90KHRocmVzaG9sZF9jb21wLGlzLmNvcnIgPSBGLG9yZGVyPSJoY2x1c3QiKSAtLT4KCjwhLS0gYGBgIC0tPgoKCgoKCgoKCgoKCgoKCg==